xfs: when replaying bmap operations, don't let unlinked inodes get reaped
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 3 Oct 2016 16:11:29 +0000 (09:11 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Tue, 4 Oct 2016 18:05:44 +0000 (11:05 -0700)
Log recovery will iget an inode to replay BUI items and iput the inode
when it's done.  Unfortunately, if the inode was unlinked, the iput
will see that i_nlink == 0 and decide to truncate & free the inode,
which prevents us from replaying subsequent BUIs.  We can't skip the
BUIs because we have to replay all the redo items to ensure that
atomic operations complete.

Since unlinked inode recovery will reap the inode anyway, we can
safely introduce a new inode flag to indicate that an inode is in this
'unlinked recovery' state and should not be auto-reaped in the
drop_inode path.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/xfs_bmap_item.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_log_recover.c
fs/xfs/xfs_mount.c
fs/xfs/xfs_super.c

index 02e8f93f03152e36d34ab6f481c68dd8a50647ec..9bf57c76623b404ebf5e1e57dd1582ad52de7c92 100644 (file)
@@ -456,6 +456,8 @@ xfs_bui_recover(
        if (error)
                goto err_inode;
 
+       if (VFS_I(ip)->i_nlink == 0)
+               xfs_iflags_set(ip, XFS_IRECOVERY);
        xfs_defer_init(&dfops, &firstfsb);
 
        /* Process deferred bmap item. */
index e08eaea6327b5c4752264c7a54996b75fd2e2447..edb453d531e14f31500b194a4c45f0039d2e57ea 100644 (file)
@@ -1850,6 +1850,7 @@ xfs_inactive(
        }
 
        mp = ip->i_mount;
+       ASSERT(!xfs_iflags_test(ip, XFS_IRECOVERY));
 
        /* If this is a read-only mount, don't do this (would generate I/O) */
        if (mp->m_flags & XFS_MOUNT_RDONLY)
index a8658e68041a37d1d036532c7cc5ff2a70fa1916..4be32036e16a995448bdb32363168a01c3585a60 100644 (file)
@@ -222,6 +222,12 @@ static inline bool xfs_is_reflink_inode(struct xfs_inode *ip)
 #define XFS_IPINNED            (1 << __XFS_IPINNED_BIT)
 #define XFS_IDONTCACHE         (1 << 9) /* don't cache the inode long term */
 #define XFS_IEOFBLOCKS         (1 << 10)/* has the preallocblocks tag set */
+/*
+ * If this unlinked inode is in the middle of recovery, don't let drop_inode
+ * truncate and free the inode.  This can happen if we iget the inode during
+ * log recovery to replay a bmap operation on the inode.
+ */
+#define XFS_IRECOVERY          (1 << 11)
 
 /*
  * Per-lifetime flags need to be reset when re-using a reclaimable inode during
index 9697e946aa35f632c505848cc1e46fdefe9d435a..9b3d7c76915d9c92948bed063d79bd10a6107a77 100644 (file)
@@ -4969,6 +4969,7 @@ xlog_recover_process_one_iunlink(
        if (error)
                goto fail_iput;
 
+       xfs_iflags_clear(ip, XFS_IRECOVERY);
        ASSERT(VFS_I(ip)->i_nlink == 0);
        ASSERT(VFS_I(ip)->i_mode != 0);
 
index 3f64615f58db91a053cec19dbf0c48381c5f5626..a78a1164bab3d553ccfd9c5dfb2bc9edd2f738bf 100644 (file)
@@ -924,6 +924,15 @@ xfs_mountfs(
                }
        }
 
+       /*
+        * During the second phase of log recovery, we need iget and
+        * iput to behave like they do for an active filesystem.
+        * xfs_fs_drop_inode needs to be able to prevent the deletion
+        * of inodes before we're done replaying log items on those
+        * inodes.
+        */
+       mp->m_super->s_flags |= MS_ACTIVE;
+
        /*
         * Finish recovering the file system.  This part needed to be delayed
         * until after the root and real-time bitmap inodes were consistently
index 204b794cebd5123e19840e3b58331a2396b34c51..35c04a7907546088a4bf980b3ac97e51c3e1dafe 100644 (file)
@@ -1008,6 +1008,16 @@ xfs_fs_drop_inode(
 {
        struct xfs_inode        *ip = XFS_I(inode);
 
+       /*
+        * If this unlinked inode is in the middle of recovery, don't
+        * drop the inode just yet; log recovery will take care of
+        * that.  See the comment for this inode flag.
+        */
+       if (ip->i_flags & XFS_IRECOVERY) {
+               ASSERT(ip->i_mount->m_log->l_flags & XLOG_RECOVERY_NEEDED);
+               return 0;
+       }
+
        return generic_drop_inode(inode) || (ip->i_flags & XFS_IDONTCACHE);
 }