Commit f0b2efad authored by Brian Foster's avatar Brian Foster Committed by Dave Chinner

xfs: don't leave EFIs on AIL on mount failure

Log recovery occurs in two phases at mount time. In the first phase,
EFIs and EFDs are processed and potentially cancelled out. EFIs without
EFD objects are inserted into the AIL for processing and recovery in the
second phase. xfs_mountfs() runs various other operations between the
phases and is thus subject to failure. If failure occurs after the first
phase but before the second, pending EFIs sit on the AIL, pin it and
cause the mount to hang.

Update the mount sequence to ensure that pending EFIs are cancelled in
the event of failure. Add a recovery cancellation mechanism to iterate
the AIL and cancel all EFI items when requested. Plumb cancellation
support through the log mount finish helper and update xfs_mountfs() to
invoke cancellation in the event of failure after recovery has started.
Signed-off-by: default avatarBrian Foster <bfoster@redhat.com>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
parent e32a1d1f
...@@ -700,6 +700,7 @@ xfs_log_mount( ...@@ -700,6 +700,7 @@ xfs_log_mount(
if (error) { if (error) {
xfs_warn(mp, "log mount/recovery failed: error %d", xfs_warn(mp, "log mount/recovery failed: error %d",
error); error);
xlog_recover_cancel(mp->m_log);
goto out_destroy_ail; goto out_destroy_ail;
} }
} }
...@@ -740,18 +741,35 @@ xfs_log_mount( ...@@ -740,18 +741,35 @@ xfs_log_mount(
* it. * it.
*/ */
int int
xfs_log_mount_finish(xfs_mount_t *mp) xfs_log_mount_finish(
struct xfs_mount *mp)
{ {
int error = 0; int error = 0;
if (!(mp->m_flags & XFS_MOUNT_NORECOVERY)) { if (mp->m_flags & XFS_MOUNT_NORECOVERY) {
error = xlog_recover_finish(mp->m_log);
if (!error)
xfs_log_work_queue(mp);
} else {
ASSERT(mp->m_flags & XFS_MOUNT_RDONLY); ASSERT(mp->m_flags & XFS_MOUNT_RDONLY);
return 0;
} }
error = xlog_recover_finish(mp->m_log);
if (!error)
xfs_log_work_queue(mp);
return error;
}
/*
* The mount has failed. Cancel the recovery if it hasn't completed and destroy
* the log.
*/
int
xfs_log_mount_cancel(
struct xfs_mount *mp)
{
int error;
error = xlog_recover_cancel(mp->m_log);
xfs_log_unmount(mp);
return error; return error;
} }
......
...@@ -147,6 +147,7 @@ int xfs_log_mount(struct xfs_mount *mp, ...@@ -147,6 +147,7 @@ int xfs_log_mount(struct xfs_mount *mp,
xfs_daddr_t start_block, xfs_daddr_t start_block,
int num_bblocks); int num_bblocks);
int xfs_log_mount_finish(struct xfs_mount *mp); int xfs_log_mount_finish(struct xfs_mount *mp);
int xfs_log_mount_cancel(struct xfs_mount *);
xfs_lsn_t xlog_assign_tail_lsn(struct xfs_mount *mp); xfs_lsn_t xlog_assign_tail_lsn(struct xfs_mount *mp);
xfs_lsn_t xlog_assign_tail_lsn_locked(struct xfs_mount *mp); xfs_lsn_t xlog_assign_tail_lsn_locked(struct xfs_mount *mp);
void xfs_log_space_wake(struct xfs_mount *mp); void xfs_log_space_wake(struct xfs_mount *mp);
......
...@@ -426,6 +426,8 @@ xlog_recover( ...@@ -426,6 +426,8 @@ xlog_recover(
extern int extern int
xlog_recover_finish( xlog_recover_finish(
struct xlog *log); struct xlog *log);
extern int
xlog_recover_cancel(struct xlog *);
extern __le32 xlog_cksum(struct xlog *log, struct xlog_rec_header *rhead, extern __le32 xlog_cksum(struct xlog *log, struct xlog_rec_header *rhead,
char *dp, int size); char *dp, int size);
......
...@@ -3791,10 +3791,10 @@ xlog_recover_process_efi( ...@@ -3791,10 +3791,10 @@ xlog_recover_process_efi(
*/ */
STATIC int STATIC int
xlog_recover_process_efis( xlog_recover_process_efis(
struct xlog *log) struct xlog *log)
{ {
xfs_log_item_t *lip; struct xfs_log_item *lip;
xfs_efi_log_item_t *efip; struct xfs_efi_log_item *efip;
int error = 0; int error = 0;
struct xfs_ail_cursor cur; struct xfs_ail_cursor cur;
struct xfs_ail *ailp; struct xfs_ail *ailp;
...@@ -3818,7 +3818,7 @@ xlog_recover_process_efis( ...@@ -3818,7 +3818,7 @@ xlog_recover_process_efis(
/* /*
* Skip EFIs that we've already processed. * Skip EFIs that we've already processed.
*/ */
efip = (xfs_efi_log_item_t *)lip; efip = container_of(lip, struct xfs_efi_log_item, efi_item);
if (test_bit(XFS_EFI_RECOVERED, &efip->efi_flags)) { if (test_bit(XFS_EFI_RECOVERED, &efip->efi_flags)) {
lip = xfs_trans_ail_cursor_next(ailp, &cur); lip = xfs_trans_ail_cursor_next(ailp, &cur);
continue; continue;
...@@ -3837,6 +3837,50 @@ xlog_recover_process_efis( ...@@ -3837,6 +3837,50 @@ xlog_recover_process_efis(
return error; return error;
} }
/*
* A cancel occurs when the mount has failed and we're bailing out. Release all
* pending EFIs so they don't pin the AIL.
*/
STATIC int
xlog_recover_cancel_efis(
struct xlog *log)
{
struct xfs_log_item *lip;
struct xfs_efi_log_item *efip;
int error = 0;
struct xfs_ail_cursor cur;
struct xfs_ail *ailp;
ailp = log->l_ailp;
spin_lock(&ailp->xa_lock);
lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
while (lip != NULL) {
/*
* We're done when we see something other than an EFI.
* There should be no EFIs left in the AIL now.
*/
if (lip->li_type != XFS_LI_EFI) {
#ifdef DEBUG
for (; lip; lip = xfs_trans_ail_cursor_next(ailp, &cur))
ASSERT(lip->li_type != XFS_LI_EFI);
#endif
break;
}
efip = container_of(lip, struct xfs_efi_log_item, efi_item);
spin_unlock(&ailp->xa_lock);
xfs_efi_release(efip);
spin_lock(&ailp->xa_lock);
lip = xfs_trans_ail_cursor_next(ailp, &cur);
}
xfs_trans_ail_cursor_done(&cur);
spin_unlock(&ailp->xa_lock);
return error;
}
/* /*
* This routine performs a transaction to null out a bad inode pointer * This routine performs a transaction to null out a bad inode pointer
* in an agi unlinked inode hash bucket. * in an agi unlinked inode hash bucket.
...@@ -4610,6 +4654,17 @@ xlog_recover_finish( ...@@ -4610,6 +4654,17 @@ xlog_recover_finish(
return 0; return 0;
} }
int
xlog_recover_cancel(
struct xlog *log)
{
int error = 0;
if (log->l_flags & XLOG_RECOVERY_NEEDED)
error = xlog_recover_cancel_efis(log);
return error;
}
#if defined(DEBUG) #if defined(DEBUG)
/* /*
......
...@@ -615,14 +615,14 @@ xfs_default_resblks(xfs_mount_t *mp) ...@@ -615,14 +615,14 @@ xfs_default_resblks(xfs_mount_t *mp)
*/ */
int int
xfs_mountfs( xfs_mountfs(
xfs_mount_t *mp) struct xfs_mount *mp)
{ {
xfs_sb_t *sbp = &(mp->m_sb); struct xfs_sb *sbp = &(mp->m_sb);
xfs_inode_t *rip; struct xfs_inode *rip;
__uint64_t resblks; __uint64_t resblks;
uint quotamount = 0; uint quotamount = 0;
uint quotaflags = 0; uint quotaflags = 0;
int error = 0; int error = 0;
xfs_sb_mount_common(mp, sbp); xfs_sb_mount_common(mp, sbp);
...@@ -799,7 +799,9 @@ xfs_mountfs( ...@@ -799,7 +799,9 @@ xfs_mountfs(
} }
/* /*
* log's mount-time initialization. Perform 1st part recovery if needed * Log's mount-time initialization. The first part of recovery can place
* some items on the AIL, to be handled when recovery is finished or
* cancelled.
*/ */
error = xfs_log_mount(mp, mp->m_logdev_targp, error = xfs_log_mount(mp, mp->m_logdev_targp,
XFS_FSB_TO_DADDR(mp, sbp->sb_logstart), XFS_FSB_TO_DADDR(mp, sbp->sb_logstart),
...@@ -910,9 +912,9 @@ xfs_mountfs( ...@@ -910,9 +912,9 @@ xfs_mountfs(
} }
/* /*
* Finish recovering the file system. This part needed to be * Finish recovering the file system. This part needed to be delayed
* delayed until after the root and real-time bitmap inodes * until after the root and real-time bitmap inodes were consistently
* were consistently read in. * read in.
*/ */
error = xfs_log_mount_finish(mp); error = xfs_log_mount_finish(mp);
if (error) { if (error) {
...@@ -956,7 +958,7 @@ xfs_mountfs( ...@@ -956,7 +958,7 @@ xfs_mountfs(
out_rele_rip: out_rele_rip:
IRELE(rip); IRELE(rip);
out_log_dealloc: out_log_dealloc:
xfs_log_unmount(mp); xfs_log_mount_cancel(mp);
out_fail_wait: out_fail_wait:
if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp) if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp)
xfs_wait_buftarg(mp->m_logdev_targp); xfs_wait_buftarg(mp->m_logdev_targp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment