]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_scrub: vectorize repair calls
authorDarrick J. Wong <djwong@kernel.org>
Mon, 29 Jul 2024 23:23:30 +0000 (16:23 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 30 Jul 2024 00:01:13 +0000 (17:01 -0700)
Use the new vectorized scrub kernel calls to reduce the overhead of
performing repairs.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
scrub/repair.c
scrub/scrub.c
scrub/scrub_private.h

index 7a710a159e69d63d28e33f2315f491382ded23e3..6a5fd40fd02f4789c421e78b8ef5d5402475c0fb 100644 (file)
 #include "descr.h"
 #include "scrub_private.h"
 
-static int repair_epilogue(struct scrub_ctx *ctx, struct descr *dsc,
-               struct scrub_item *sri, unsigned int repair_flags,
-               const struct xfs_scrub_vec *oldm,
-               const struct xfs_scrub_vec *meta);
-
 /* General repair routines. */
 
 /*
@@ -83,64 +78,14 @@ repair_want_service_downgrade(
        return false;
 }
 
-/* Repair some metadata. */
-static int
-xfs_repair_metadata(
-       struct scrub_ctx                *ctx,
-       struct xfs_fd                   *xfdp,
-       unsigned int                    scrub_type,
-       struct scrub_item               *sri,
-       unsigned int                    repair_flags)
+static inline void
+restore_oldvec(
+       struct xfs_scrub_vec    *oldvec,
+       const struct scrub_item *sri,
+       unsigned int            scrub_type)
 {
-       struct xfs_scrub_metadata       meta = { 0 };
-       struct xfs_scrub_vec            oldm, vec;
-       DEFINE_DESCR(dsc, ctx, format_scrub_descr);
-       bool                            repair_only;
-
-       /*
-        * If the caller boosted the priority of this scrub type on behalf of a
-        * higher level repair by setting IFLAG_REPAIR, turn off REPAIR_ONLY.
-        */
-       repair_only = (repair_flags & XRM_REPAIR_ONLY) &&
-                       scrub_item_type_boosted(sri, scrub_type);
-
-       assert(scrub_type < XFS_SCRUB_TYPE_NR);
-       assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
-       meta.sm_type = scrub_type;
-       meta.sm_flags = XFS_SCRUB_IFLAG_REPAIR;
-       if (use_force_rebuild)
-               meta.sm_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
-       switch (xfrog_scrubbers[scrub_type].group) {
-       case XFROG_SCRUB_GROUP_AGHEADER:
-       case XFROG_SCRUB_GROUP_PERAG:
-               meta.sm_agno = sri->sri_agno;
-               break;
-       case XFROG_SCRUB_GROUP_INODE:
-               meta.sm_ino = sri->sri_ino;
-               meta.sm_gen = sri->sri_gen;
-               break;
-       default:
-               break;
-       }
-
-       vec.sv_type = scrub_type;
-       vec.sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
-       memcpy(&oldm, &vec, sizeof(struct xfs_scrub_vec));
-       if (!is_corrupt(&vec) && repair_only)
-               return 0;
-
-       descr_set(&dsc, &meta);
-
-       if (needs_repair(&vec))
-               str_info(ctx, descr_render(&dsc), _("Attempting repair."));
-       else if (debug || verbose)
-               str_info(ctx, descr_render(&dsc),
-                               _("Attempting optimization."));
-
-       vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta);
-       vec.sv_flags = meta.sm_flags;
-
-       return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &vec);
+       oldvec->sv_type = scrub_type;
+       oldvec->sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY;
 }
 
 static int
@@ -149,12 +94,15 @@ repair_epilogue(
        struct descr                    *dsc,
        struct scrub_item               *sri,
        unsigned int                    repair_flags,
-       const struct xfs_scrub_vec      *oldm,
        const struct xfs_scrub_vec      *meta)
 {
+       struct xfs_scrub_vec            oldv;
+       struct xfs_scrub_vec            *oldm = &oldv;
        unsigned int                    scrub_type = meta->sv_type;
        int                             error = -meta->sv_ret;
 
+       restore_oldvec(oldm, sri, meta->sv_type);
+
        switch (error) {
        case 0:
                /* No operational errors encountered. */
@@ -296,6 +244,133 @@ _("Repair unsuccessful; offline repair required."));
        return 0;
 }
 
+/* Decide if the dependent scrub types of the given scrub type are ok. */
+static bool
+repair_item_dependencies_ok(
+       const struct scrub_item *sri,
+       unsigned int            scrub_type)
+{
+       unsigned int            dep_mask = repair_deps[scrub_type];
+       unsigned int            b;
+
+       for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
+               if (!(dep_mask & 1))
+                       continue;
+               /*
+                * If this lower level object also needs repair, we can't fix
+                * the higher level item.
+                */
+               if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR)
+                       return false;
+       }
+
+       return true;
+}
+
+/* Decide if we want to repair a particular type of metadata. */
+static bool
+can_repair_now(
+       const struct scrub_item *sri,
+       unsigned int            scrub_type,
+       __u32                   repair_mask,
+       unsigned int            repair_flags)
+{
+       struct xfs_scrub_vec    oldvec;
+       bool                    repair_only;
+
+       /* Do we even need to repair this thing? */
+       if (!(sri->sri_state[scrub_type] & repair_mask))
+               return false;
+
+       restore_oldvec(&oldvec, sri, scrub_type);
+
+       /*
+        * If the caller boosted the priority of this scrub type on behalf of a
+        * higher level repair by setting IFLAG_REPAIR, ignore REPAIR_ONLY.
+        */
+       repair_only = (repair_flags & XRM_REPAIR_ONLY) &&
+                     !(sri->sri_state[scrub_type] & SCRUB_ITEM_BOOST_REPAIR);
+       if (!is_corrupt(&oldvec) && repair_only)
+               return false;
+
+       /*
+        * Don't try to repair higher level items if their lower-level
+        * dependencies haven't been verified, unless this is our last chance
+        * to fix things without complaint.
+        */
+       if (!(repair_flags & XRM_FINAL_WARNING) &&
+           !repair_item_dependencies_ok(sri, scrub_type))
+               return false;
+
+       return true;
+}
+
+/*
+ * Repair some metadata.
+ *
+ * Returns 0 for success (or repair item deferral), or ECANCELED to abort the
+ * program.
+ */
+static int
+repair_call_kernel(
+       struct scrub_ctx                *ctx,
+       struct xfs_fd                   *xfdp,
+       struct scrub_item               *sri,
+       __u32                           repair_mask,
+       unsigned int                    repair_flags)
+{
+       DEFINE_DESCR(dsc, ctx, format_scrubv_descr);
+       struct xfrog_scrubv             scrubv = { };
+       struct scrubv_descr             vdesc = SCRUBV_DESCR(&scrubv);
+       struct xfs_scrub_vec            *v;
+       unsigned int                    scrub_type;
+       int                             error;
+
+       assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
+
+       xfrog_scrubv_from_item(&scrubv, sri);
+       descr_set(&dsc, &vdesc);
+
+       foreach_scrub_type(scrub_type) {
+               if (scrub_excessive_errors(ctx))
+                       return ECANCELED;
+
+               if (!can_repair_now(sri, scrub_type, repair_mask,
+                                       repair_flags))
+                       continue;
+
+               xfrog_scrubv_add_item(&scrubv, sri, scrub_type, true);
+
+               if (sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSREPAIR)
+                       str_info(ctx, descr_render(&dsc),
+                                       _("Attempting repair."));
+               else if (debug || verbose)
+                       str_info(ctx, descr_render(&dsc),
+                                       _("Attempting optimization."));
+
+               dbg_printf("repair %s flags %xh tries %u\n", descr_render(&dsc),
+                               sri->sri_state[scrub_type],
+                               sri->sri_tries[scrub_type]);
+       }
+
+       error = -xfrog_scrubv_metadata(xfdp, &scrubv);
+       if (error)
+               return error;
+
+       foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, v) {
+               error = repair_epilogue(ctx, &dsc, sri, repair_flags, v);
+               if (error)
+                       return error;
+
+               /* Maybe update progress if we fixed the problem. */
+               if (!(repair_flags & XRM_NOPROGRESS) &&
+                   !(sri->sri_state[v->sv_type] & SCRUB_ITEM_REPAIR_ANY))
+                       progress_add(1);
+       }
+
+       return 0;
+}
+
 /*
  * Prioritize action items in order of how long we can wait.
  *
@@ -632,29 +707,6 @@ action_list_process(
        return ret;
 }
 
-/* Decide if the dependent scrub types of the given scrub type are ok. */
-static bool
-repair_item_dependencies_ok(
-       const struct scrub_item *sri,
-       unsigned int            scrub_type)
-{
-       unsigned int            dep_mask = repair_deps[scrub_type];
-       unsigned int            b;
-
-       for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) {
-               if (!(dep_mask & 1))
-                       continue;
-               /*
-                * If this lower level object also needs repair, we can't fix
-                * the higher level item.
-                */
-               if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR)
-                       return false;
-       }
-
-       return true;
-}
-
 /*
  * For a given filesystem object, perform all repairs of a given class
  * (corrupt, xcorrupt, xfail, preen) if the repair item says it's needed.
@@ -670,13 +722,14 @@ repair_item_class(
        struct xfs_fd                   xfd;
        struct scrub_item               old_sri;
        struct xfs_fd                   *xfdp = &ctx->mnt;
-       unsigned int                    scrub_type;
        int                             error = 0;
 
        if (ctx->mode == SCRUB_MODE_DRY_RUN)
                return 0;
        if (ctx->mode == SCRUB_MODE_PREEN && !(repair_mask & SCRUB_ITEM_PREEN))
                return 0;
+       if (!scrub_item_schedule_work(sri, repair_mask))
+               return 0;
 
        /*
         * If the caller passed us a file descriptor for a scrub, use it
@@ -689,39 +742,14 @@ repair_item_class(
                xfdp = &xfd;
        }
 
-       foreach_scrub_type(scrub_type) {
-               if (scrub_excessive_errors(ctx))
-                       return ECANCELED;
-
-               if (!(sri->sri_state[scrub_type] & repair_mask))
-                       continue;
-
-               /*
-                * Don't try to repair higher level items if their lower-level
-                * dependencies haven't been verified, unless this is our last
-                * chance to fix things without complaint.
-                */
-               if (!(flags & XRM_FINAL_WARNING) &&
-                   !repair_item_dependencies_ok(sri, scrub_type))
-                       continue;
+       do {
+               memcpy(&old_sri, sri, sizeof(struct scrub_item));
+               error = repair_call_kernel(ctx, xfdp, sri, repair_mask, flags);
+               if (error)
+                       return error;
+       } while (scrub_item_call_kernel_again(sri, repair_mask, &old_sri));
 
-               sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
-               do {
-                       memcpy(&old_sri, sri, sizeof(old_sri));
-                       error = xfs_repair_metadata(ctx, xfdp, scrub_type, sri,
-                                       flags);
-                       if (error)
-                               return error;
-               } while (scrub_item_call_kernel_again(sri, scrub_type,
-                                       repair_mask, &old_sri));
-
-               /* Maybe update progress if we fixed the problem. */
-               if (!(flags & XRM_NOPROGRESS) &&
-                   !(sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY))
-                       progress_add(1);
-       }
-
-       return error;
+       return 0;
 }
 
 /*
index 0c77f947244a3f080ff2ff49968e5c2556042792..d582dafbbe4ef341101684685fa6e4c6285611d0 100644 (file)
@@ -64,35 +64,6 @@ format_scrubv_descr(
        return -1;
 }
 
-/* Format a scrub description. */
-int
-format_scrub_descr(
-       struct scrub_ctx                *ctx,
-       char                            *buf,
-       size_t                          buflen,
-       void                            *where)
-{
-       struct xfs_scrub_metadata       *meta = where;
-       const struct xfrog_scrub_descr  *sc = &xfrog_scrubbers[meta->sm_type];
-
-       switch (sc->group) {
-       case XFROG_SCRUB_GROUP_AGHEADER:
-       case XFROG_SCRUB_GROUP_PERAG:
-               return snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno,
-                               _(sc->descr));
-       case XFROG_SCRUB_GROUP_INODE:
-               return scrub_render_ino_descr(ctx, buf, buflen,
-                               meta->sm_ino, meta->sm_gen, "%s",
-                               _(sc->descr));
-       case XFROG_SCRUB_GROUP_FS:
-       case XFROG_SCRUB_GROUP_SUMMARY:
-       case XFROG_SCRUB_GROUP_ISCAN:
-       case XFROG_SCRUB_GROUP_NONE:
-               return snprintf(buf, buflen, _("%s"), _(sc->descr));
-       }
-       return -1;
-}
-
 /* Warn about strange circumstances after scrub. */
 void
 scrub_warn_incomplete_scrub(
@@ -271,12 +242,17 @@ void
 xfrog_scrubv_add_item(
        struct xfrog_scrubv             *scrubv,
        const struct scrub_item         *sri,
-       unsigned int                    scrub_type)
+       unsigned int                    scrub_type,
+       bool                            want_repair)
 {
        struct xfs_scrub_vec            *v;
 
        v = xfrog_scrubv_next_vector(scrubv);
        v->sv_type = scrub_type;
+       if (want_repair)
+               v->sv_flags |= XFS_SCRUB_IFLAG_REPAIR;
+       if (want_repair && use_force_rebuild)
+               v->sv_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD;
 }
 
 /* Do a read-only check of some metadata. */
@@ -301,7 +277,7 @@ scrub_call_kernel(
        foreach_scrub_type(scrub_type) {
                if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
                        continue;
-               xfrog_scrubv_add_item(&scrubv, sri, scrub_type);
+               xfrog_scrubv_add_item(&scrubv, sri, scrub_type, false);
 
                dbg_printf("check %s flags %xh tries %u\n", descr_render(&dsc),
                                sri->sri_state[scrub_type],
@@ -365,8 +341,8 @@ scrub_item_schedule_group(
 }
 
 /* Decide if we call the kernel again to finish scrub/repair activity. */
-static inline bool
-scrub_item_call_kernel_again_future(
+bool
+scrub_item_call_kernel_again(
        struct scrub_item       *sri,
        uint8_t                 work_mask,
        const struct scrub_item *old)
@@ -382,6 +358,11 @@ scrub_item_call_kernel_again_future(
        if (!nr)
                return false;
 
+       /*
+        * We are willing to go again if the last call had any effect on the
+        * state of the scrub item that the caller cares about or if the kernel
+        * asked us to try again.
+        */
        foreach_scrub_type(scrub_type) {
                uint8_t         statex = sri->sri_state[scrub_type] ^
                                         old->sri_state[scrub_type];
@@ -395,34 +376,6 @@ scrub_item_call_kernel_again_future(
        return false;
 }
 
-/* Decide if we call the kernel again to finish scrub/repair activity. */
-bool
-scrub_item_call_kernel_again(
-       struct scrub_item       *sri,
-       unsigned int            scrub_type,
-       uint8_t                 work_mask,
-       const struct scrub_item *old)
-{
-       uint8_t                 statex;
-
-       /* If there's nothing to do, we're done. */
-       if (!(sri->sri_state[scrub_type] & work_mask))
-               return false;
-
-       /*
-        * We are willing to go again if the last call had any effect on the
-        * state of the scrub item that the caller cares about, if the freeze
-        * flag got set, or if the kernel asked us to try again...
-        */
-       statex = sri->sri_state[scrub_type] ^ old->sri_state[scrub_type];
-       if (statex & work_mask)
-               return true;
-       if (sri->sri_tries[scrub_type] != old->sri_tries[scrub_type])
-               return true;
-
-       return false;
-}
-
 /*
  * For each scrub item whose state matches the state_flags, set up the item
  * state for a kernel call.  Returns true if any work was scheduled.
@@ -477,7 +430,7 @@ scrub_item_check_file(
                error = scrub_call_kernel(ctx, xfdp, sri);
                if (error)
                        return error;
-       } while (scrub_item_call_kernel_again_future(sri, SCRUB_ITEM_NEEDSCHECK,
+       } while (scrub_item_call_kernel_again(sri, SCRUB_ITEM_NEEDSCHECK,
                                &old_sri));
 
        return 0;
index bf53ee5af2cf6162a47d4593074d87c5b0dd20ae..d9de18ce17957bd6cdcac7e6704ced321ce002a4 100644 (file)
 void xfrog_scrubv_from_item(struct xfrog_scrubv *scrubv,
                const struct scrub_item *sri);
 void xfrog_scrubv_add_item(struct xfrog_scrubv *scrubv,
-               const struct scrub_item *sri, unsigned int scrub_type);
-
-int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen,
-               void *where);
+               const struct scrub_item *sri, unsigned int scrub_type,
+               bool want_repair);
 
 struct scrubv_descr {
        struct xfrog_scrubv     *scrubv;
@@ -116,8 +114,7 @@ scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type)
        return true;
 }
 
-bool scrub_item_call_kernel_again(struct scrub_item *sri,
-               unsigned int scrub_type, uint8_t work_mask,
+bool scrub_item_call_kernel_again(struct scrub_item *sri, uint8_t work_mask,
                const struct scrub_item *old);
 bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags);