xfs: make inode unlinked bucket recovery work with quotacheck
authorDarrick J. Wong <djwong@kernel.org>
Mon, 11 Sep 2023 15:39:08 +0000 (08:39 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 12 Sep 2023 17:31:07 +0000 (10:31 -0700)
Teach quotacheck to reload the unlinked inode lists when walking the
inode table.  This requires extra state handling, since it's possible
that a reloaded inode will get inactivated before quotacheck tries to
scan it; in this case, we need to ensure that the reloaded inode does
not have dquots attached when it is freed.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
fs/xfs/xfs_attr_inactive.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_mount.h
fs/xfs/xfs_qm.c

index 5db87b3..89c7a9f 100644 (file)
@@ -333,7 +333,6 @@ xfs_attr_inactive(
        int                     error = 0;
 
        mp = dp->i_mount;
-       ASSERT(! XFS_NOT_DQATTACHED(mp, dp));
 
        xfs_ilock(dp, lock_mode);
        if (!xfs_inode_has_attr_fork(dp))
index 2fd22db..f94f7b3 100644 (file)
@@ -1742,9 +1742,13 @@ xfs_inactive(
             ip->i_df.if_nextents > 0 || ip->i_delayed_blks > 0))
                truncate = 1;
 
-       error = xfs_qm_dqattach(ip);
-       if (error)
-               goto out;
+       if (xfs_iflags_test(ip, XFS_IQUOTAUNCHECKED)) {
+               xfs_qm_dqdetach(ip);
+       } else {
+               error = xfs_qm_dqattach(ip);
+               if (error)
+                       goto out;
+       }
 
        if (S_ISLNK(VFS_I(ip)->i_mode))
                error = xfs_inactive_symlink(ip);
@@ -1962,6 +1966,8 @@ xfs_iunlink_reload_next(
        trace_xfs_iunlink_reload_next(next_ip);
 rele:
        ASSERT(!(VFS_I(next_ip)->i_state & I_DONTCACHE));
+       if (xfs_is_quotacheck_running(mp) && next_ip)
+               xfs_iflags_set(next_ip, XFS_IQUOTAUNCHECKED);
        xfs_irele(next_ip);
        return error;
 }
index a111b55..0c5bdb9 100644 (file)
@@ -344,6 +344,9 @@ static inline bool xfs_inode_has_large_extent_counts(struct xfs_inode *ip)
  */
 #define XFS_INACTIVATING       (1 << 13)
 
+/* Quotacheck is running but inode has not been added to quota counts. */
+#define XFS_IQUOTAUNCHECKED    (1 << 14)
+
 /* All inode state flags related to inode reclaim. */
 #define XFS_ALL_IRECLAIM_FLAGS (XFS_IRECLAIMABLE | \
                                 XFS_IRECLAIM | \
@@ -358,7 +361,7 @@ static inline bool xfs_inode_has_large_extent_counts(struct xfs_inode *ip)
 #define XFS_IRECLAIM_RESET_FLAGS       \
        (XFS_IRECLAIMABLE | XFS_IRECLAIM | \
         XFS_IDIRTY_RELEASE | XFS_ITRUNCATED | XFS_NEED_INACTIVE | \
-        XFS_INACTIVATING)
+        XFS_INACTIVATING | XFS_IQUOTAUNCHECKED)
 
 /*
  * Flags for inode locking.
index 6e28066..d19cca0 100644 (file)
@@ -405,6 +405,8 @@ __XFS_HAS_FEAT(nouuid, NOUUID)
 #define XFS_OPSTATE_WARNED_SHRINK      8
 /* Kernel has logged a warning about logged xattr updates being used. */
 #define XFS_OPSTATE_WARNED_LARP                9
+/* Mount time quotacheck is running */
+#define XFS_OPSTATE_QUOTACHECK_RUNNING 10
 
 #define __XFS_IS_OPSTATE(name, NAME) \
 static inline bool xfs_is_ ## name (struct xfs_mount *mp) \
@@ -427,6 +429,11 @@ __XFS_IS_OPSTATE(inode32, INODE32)
 __XFS_IS_OPSTATE(readonly, READONLY)
 __XFS_IS_OPSTATE(inodegc_enabled, INODEGC_ENABLED)
 __XFS_IS_OPSTATE(blockgc_enabled, BLOCKGC_ENABLED)
+#ifdef CONFIG_XFS_QUOTA
+__XFS_IS_OPSTATE(quotacheck_running, QUOTACHECK_RUNNING)
+#else
+# define xfs_is_quotacheck_running(mp) (false)
+#endif
 
 static inline bool
 xfs_should_warn(struct xfs_mount *mp, long nr)
@@ -444,7 +451,8 @@ xfs_should_warn(struct xfs_mount *mp, long nr)
        { (1UL << XFS_OPSTATE_BLOCKGC_ENABLED),         "blockgc" }, \
        { (1UL << XFS_OPSTATE_WARNED_SCRUB),            "wscrub" }, \
        { (1UL << XFS_OPSTATE_WARNED_SHRINK),           "wshrink" }, \
-       { (1UL << XFS_OPSTATE_WARNED_LARP),             "wlarp" }
+       { (1UL << XFS_OPSTATE_WARNED_LARP),             "wlarp" }, \
+       { (1UL << XFS_OPSTATE_QUOTACHECK_RUNNING),      "quotacheck" }
 
 /*
  * Max and min values for mount-option defined I/O
index 6abcc34..7256090 100644 (file)
@@ -1160,6 +1160,10 @@ xfs_qm_dqusage_adjust(
        if (error)
                return error;
 
+       error = xfs_inode_reload_unlinked(ip);
+       if (error)
+               goto error0;
+
        ASSERT(ip->i_delayed_blks == 0);
 
        if (XFS_IS_REALTIME_INODE(ip)) {
@@ -1173,6 +1177,7 @@ xfs_qm_dqusage_adjust(
        }
 
        nblks = (xfs_qcnt_t)ip->i_nblocks - rtblks;
+       xfs_iflags_clear(ip, XFS_IQUOTAUNCHECKED);
 
        /*
         * Add the (disk blocks and inode) resources occupied by this
@@ -1319,8 +1324,10 @@ xfs_qm_quotacheck(
                flags |= XFS_PQUOTA_CHKD;
        }
 
+       xfs_set_quotacheck_running(mp);
        error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0, true,
                        NULL);
+       xfs_clear_quotacheck_running(mp);
 
        /*
         * On error, the inode walk may have partially populated the dquot