]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xfs: compute file mapping reap limits dynamically
authorDarrick J. Wong <djwong@kernel.org>
Tue, 8 Apr 2025 23:14:35 +0000 (16:14 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Fri, 5 Sep 2025 15:48:22 +0000 (08:48 -0700)
Reaping file fork mappings is a little different -- log recovery can
free the blocks for us, so we only try to process a single mapping at a
time.  Therefore, we only need to figure out the maximum number of
blocks that we can invalidate in a single transaction.

The rough calculation here is:

nr_extents = (logres - reservation used by any one step) /
(space used per binval)

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/scrub/reap.c
fs/xfs/scrub/trace.h

index b2f089e2c49daa1d752f8dab684ff5fce64146dc..d58fd57aaebb43af170e06a203ff658395ae79ee 100644 (file)
@@ -542,7 +542,7 @@ xreap_configure_limits(
                return;
        }
 
-       rs->max_deferred = res / variable_overhead;
+       rs->max_deferred = per_intent ? res / variable_overhead : 0;
        res -= rs->max_deferred * per_intent;
        rs->max_binval = per_binval ? res / per_binval : 0;
 }
@@ -1446,7 +1446,7 @@ xrep_reap_bmapi_iter(
                                imap->br_blockcount);
 
                /*
-                * Schedule removal of the mapping from the fork.  We use
+                * t0: Schedule removal of the mapping from the fork.  We use
                 * deferred log intents in this function to control the exact
                 * sequence of metadata updates.
                 */
@@ -1479,8 +1479,8 @@ xrep_reap_bmapi_iter(
                return error;
 
        /*
-        * Schedule removal of the mapping from the fork.  We use deferred log
-        * intents in this function to control the exact sequence of metadata
+        * t1: Schedule removal of the mapping from the fork.  We use deferred
+        * work in this function to control the exact sequence of metadata
         * updates.
         */
        xfs_bmap_unmap_extent(sc->tp, rs->ip, rs->whichfork, imap);
@@ -1491,6 +1491,105 @@ xrep_reap_bmapi_iter(
                        XFS_FREE_EXTENT_SKIP_DISCARD);
 }
 
+/* Compute the maximum mapcount of a file buffer. */
+static unsigned int
+xreap_bmapi_binval_mapcount(
+       struct xfs_scrub        *sc)
+{
+       /* directory blocks can span multiple fsblocks and be discontiguous */
+       if (sc->sm->sm_type == XFS_SCRUB_TYPE_DIR)
+               return sc->mp->m_dir_geo->fsbcount;
+
+       /* all other file xattr/symlink blocks must be contiguous */
+       return 1;
+}
+
+/* Compute the maximum block size of a file buffer. */
+static unsigned int
+xreap_bmapi_binval_blocksize(
+       struct xfs_scrub        *sc)
+{
+       switch (sc->sm->sm_type) {
+       case XFS_SCRUB_TYPE_DIR:
+               return sc->mp->m_dir_geo->blksize;
+       case XFS_SCRUB_TYPE_XATTR:
+       case XFS_SCRUB_TYPE_PARENT:
+               /*
+                * The xattr structure itself consists of single fsblocks, but
+                * there could be remote xattr blocks to invalidate.
+                */
+               return XFS_XATTR_SIZE_MAX;
+       }
+
+       /* everything else is a single block */
+       return sc->mp->m_sb.sb_blocksize;
+}
+
+/*
+ * Compute the maximum number of buffer invalidations that we can do while
+ * reaping a single extent from a file fork.
+ */
+STATIC void
+xreap_configure_bmapi_limits(
+       struct xreap_state      *rs)
+{
+       struct xfs_scrub        *sc = rs->sc;
+       struct xfs_mount        *mp = sc->mp;
+
+       /* overhead of invalidating a buffer */
+       const unsigned int      per_binval =
+               xfs_buf_inval_log_space(xreap_bmapi_binval_mapcount(sc),
+                                           xreap_bmapi_binval_blocksize(sc));
+
+       /*
+        * In the worst case, relogging an intent item causes both an intent
+        * item and a done item to be attached to a transaction for each extent
+        * that we'd like to process.
+        */
+       const unsigned int      efi = xfs_efi_log_space(1) +
+                                     xfs_efd_log_space(1);
+       const unsigned int      rui = xfs_rui_log_space(1) +
+                                     xfs_rud_log_space();
+       const unsigned int      bui = xfs_bui_log_space(1) +
+                                     xfs_bud_log_space();
+
+       /*
+        * t1: Unmapping crosslinked file data blocks: one bmap deletion,
+        * possibly an EFI for underfilled bmbt blocks, and an rmap deletion.
+        *
+        * t2: Freeing freeing file data blocks: one bmap deletion, possibly an
+        * EFI for underfilled bmbt blocks, and another EFI for the space
+        * itself.
+        */
+       const unsigned int      t1 = (bui + efi) + rui;
+       const unsigned int      t2 = (bui + efi) + efi;
+       const unsigned int      per_intent = max(t1, t2);
+
+       /*
+        * For each transaction in a reap chain, we must be able to take one
+        * step in the defer item chain, which should only consist of CUI, EFI,
+        * or RUI items.
+        */
+       const unsigned int      f1 = xfs_calc_finish_efi_reservation(mp, 1);
+       const unsigned int      f2 = xfs_calc_finish_rui_reservation(mp, 1);
+       const unsigned int      f3 = xfs_calc_finish_bui_reservation(mp, 1);
+       const unsigned int      step_size = max3(f1, f2, f3);
+
+       /*
+        * Each call to xreap_ifork_extent starts with a clean transaction and
+        * operates on a single mapping by creating a chain of log intent items
+        * for that mapping.  We need to leave enough reservation in the
+        * transaction to log btree buffer and inode updates for each step in
+        * the chain, and to relog the log intents.
+        */
+       const unsigned int      per_extent_res = per_intent + step_size;
+
+       xreap_configure_limits(rs, per_extent_res, per_binval, 0, per_binval);
+
+       trace_xreap_bmapi_limits(sc->tp, per_binval, rs->max_binval,
+                       step_size, per_intent, 1);
+}
+
 /*
  * Dispose of as much of this file extent as we can.  Upon successful return,
  * the imap will reflect the mapping that was removed from the fork.
@@ -1554,7 +1653,6 @@ xrep_reap_ifork(
                .sc             = sc,
                .ip             = ip,
                .whichfork      = whichfork,
-               .max_binval     = XREAP_MAX_BINVAL,
        };
        xfs_fileoff_t           off = 0;
        int                     bmap_flags = xfs_bmapi_aflag(whichfork);
@@ -1564,6 +1662,7 @@ xrep_reap_ifork(
        ASSERT(ip == sc->ip || ip == sc->tempip);
        ASSERT(whichfork == XFS_ATTR_FORK || !XFS_IS_REALTIME_INODE(ip));
 
+       xreap_configure_bmapi_limits(&rs);
        while (off < XFS_MAX_FILEOFF) {
                struct xfs_bmbt_irec    imap;
                int                     nimaps = 1;
index 1a994d339c42cf9b624d2b926591a22f8acf63ef..39ea651cbb7510db97a5fe8a5464d3b2cc81e21f 100644 (file)
@@ -2043,6 +2043,7 @@ DEFINE_EVENT(xrep_reap_limits_class, name, \
 DEFINE_REPAIR_REAP_LIMITS_EVENT(xreap_agextent_limits);
 DEFINE_REPAIR_REAP_LIMITS_EVENT(xreap_agcow_limits);
 DEFINE_REPAIR_REAP_LIMITS_EVENT(xreap_rgcow_limits);
+DEFINE_REPAIR_REAP_LIMITS_EVENT(xreap_bmapi_limits);
 
 DECLARE_EVENT_CLASS(xrep_reap_find_class,
        TP_PROTO(const struct xfs_group *xg, xfs_agblock_t agbno,