]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
xfs: don't leave EFIs on AIL on mount failure
authorBrian Foster <bfoster@redhat.com>
Tue, 18 Aug 2015 23:58:36 +0000 (09:58 +1000)
committerJack Vogel <jack.vogel@oracle.com>
Mon, 9 Apr 2018 23:01:34 +0000 (16:01 -0700)
[ Upstream commit f0b2efad16e78623b5a156f6e4e9166907b83155 ]

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.

Orabug: 27609404
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: wen.gang.wang@oracle.com
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
fs/xfs/xfs_log.c
fs/xfs/xfs_log.h
fs/xfs/xfs_log_priv.h
fs/xfs/xfs_log_recover.c
fs/xfs/xfs_mount.c

index 4e2204200e5a83ccbb1fc6e962dcfc1e0c96291c..d4cd7b08727f79aa1181a677d921e45380ad5b40 100644 (file)
@@ -703,6 +703,7 @@ xfs_log_mount(
                if (error) {
                        xfs_warn(mp, "log mount/recovery failed: error %d",
                                error);
+                       xlog_recover_cancel(mp->m_log);
                        goto out_destroy_ail;
                }
        }
@@ -743,26 +744,35 @@ out:
  * it.
  */
 int
-xfs_log_mount_finish(xfs_mount_t *mp)
-{
+xfs_log_mount_finish(
+       struct xfs_mount        *mp)
+ {
        int     error = 0;
-       int     readonly = (mp->m_flags & XFS_MOUNT_RDONLY);
 
-       if (!(mp->m_flags & XFS_MOUNT_NORECOVERY)) {
-               if (readonly) {
-                       /* Allow unlinked processing to proceed */
-                       mp->m_flags &= ~XFS_MOUNT_RDONLY;
-               }
-               error = xlog_recover_finish(mp->m_log);
-               if (!error)
-                       xfs_log_work_queue(mp);
-       } else {
+       if (mp->m_flags & XFS_MOUNT_NORECOVERY) {
                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;
 
-       if (readonly)
-               mp->m_flags |= XFS_MOUNT_RDONLY;
+       error = xlog_recover_cancel(mp->m_log);
+       xfs_log_unmount(mp);
 
        return error;
 }
index 156bcf318f79afdb85a05e865925b6b1b3a9ead9..e8d892ad6df122765c2be5c931bc5462e522152a 100644 (file)
@@ -156,6 +156,7 @@ int   xfs_log_mount(struct xfs_mount        *mp,
                        xfs_daddr_t             start_block,
                        int                     num_bblocks);
 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_locked(struct xfs_mount *mp);
 void     xfs_log_space_wake(struct xfs_mount *mp);
index fcc11b614d298f634c3e524f80a526362d7658a4..7a508ed93a418421c3a6ff1ed58e0448d847af57 100644 (file)
@@ -426,6 +426,8 @@ xlog_recover(
 extern int
 xlog_recover_finish(
        struct xlog             *log);
+extern int
+xlog_recover_cancel(struct xlog *);
 
 extern __le32   xlog_cksum(struct xlog *log, struct xlog_rec_header *rhead,
                            char *dp, int size);
index 7c88b7b85b8870019ff0f64d21bfb2b7002b3c8b..599232f2f0f31ef54a3e5b1e2163be563a30f15b 100644 (file)
@@ -3787,10 +3787,10 @@ abort_error:
  */
 STATIC int
 xlog_recover_process_efis(
-       struct xlog     *log)
+       struct xlog             *log)
 {
-       xfs_log_item_t          *lip;
-       xfs_efi_log_item_t      *efip;
+       struct xfs_log_item     *lip;
+       struct xfs_efi_log_item *efip;
        int                     error = 0;
        struct xfs_ail_cursor   cur;
        struct xfs_ail          *ailp;
@@ -3814,7 +3814,7 @@ xlog_recover_process_efis(
                /*
                 * 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)) {
                        lip = xfs_trans_ail_cursor_next(ailp, &cur);
                        continue;
@@ -3833,6 +3833,50 @@ out:
        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
  * in an agi unlinked inode hash bucket.
@@ -4616,6 +4660,17 @@ xlog_recover_finish(
        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)
 /*
index 1eedb66e88bcc9962fa4e5e32dd495264fc16130..1844b2da6a28db208473fbe8a4531eeac12478fc 100644 (file)
@@ -617,14 +617,14 @@ xfs_default_resblks(xfs_mount_t *mp)
  */
 int
 xfs_mountfs(
-       xfs_mount_t     *mp)
+       struct xfs_mount        *mp)
 {
-       xfs_sb_t        *sbp = &(mp->m_sb);
-       xfs_inode_t     *rip;
-       __uint64_t      resblks;
-       uint            quotamount = 0;
-       uint            quotaflags = 0;
-       int             error = 0;
+       struct xfs_sb           *sbp = &(mp->m_sb);
+       struct xfs_inode        *rip;
+       __uint64_t              resblks;
+       uint                    quotamount = 0;
+       uint                    quotaflags = 0;
+       int                     error = 0;
 
        xfs_sb_mount_common(mp, sbp);
 
@@ -798,7 +798,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,
                              XFS_FSB_TO_DADDR(mp, sbp->sb_logstart),
@@ -909,9 +911,9 @@ xfs_mountfs(
        }
 
        /*
-        * Finish recovering the file system.  This part needed to be
-        * delayed until after the root and real-time bitmap inodes
-        * were consistently read in.
+        * Finish recovering the file system.  This part needed to be delayed
+        * until after the root and real-time bitmap inodes were consistently
+        * read in.
         */
        error = xfs_log_mount_finish(mp);
        if (error) {
@@ -956,7 +958,7 @@ xfs_mountfs(
        IRELE(rip);
  out_log_dealloc:
        mp->m_flags |= XFS_MOUNT_UNMOUNTING;
-       xfs_log_unmount(mp);
+       xfs_log_mount_cancel(mp);
  out_fail_wait:
        if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp)
                xfs_wait_buftarg(mp->m_logdev_targp);