]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_scrub: vectorize scrub 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 kernel scrub calls to reduce the overhead of
checking metadata.

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

index 095c045915a706e4ad808409badb26d1ff2ad7c4..091b59e57e7be224bbc5efd9e93959a202adc7ee 100644 (file)
@@ -216,6 +216,8 @@ _("Kernel metadata scrubbing facility is not available."));
                return ECANCELED;
        }
 
+       check_scrubv(ctx);
+
        /*
         * Normally, callers are required to pass -n if the provided path is a
         * readonly filesystem or the kernel wasn't built with online repair
index 2fb2293558e5dfc113fdc603ad307f461a235d5d..0c77f947244a3f080ff2ff49968e5c2556042792 100644 (file)
 #include "descr.h"
 #include "scrub_private.h"
 
-static int scrub_epilogue(struct scrub_ctx *ctx, struct descr *dsc,
-               struct scrub_item *sri, struct xfs_scrub_vec *vec);
-
 /* Online scrub and repair wrappers. */
 
+/* Describe the current state of a vectored scrub. */
+int
+format_scrubv_descr(
+       struct scrub_ctx                *ctx,
+       char                            *buf,
+       size_t                          buflen,
+       void                            *where)
+{
+       struct scrubv_descr             *vdesc = where;
+       struct xfrog_scrubv             *scrubv = vdesc->scrubv;
+       struct xfs_scrub_vec_head       *vhead = &scrubv->head;
+       const struct xfrog_scrub_descr  *sc;
+       unsigned int                    scrub_type;
+
+       if (vdesc->idx >= 0)
+               scrub_type = scrubv->vectors[vdesc->idx].sv_type;
+       else if (scrubv->head.svh_nr > 0)
+               scrub_type = scrubv->vectors[scrubv->head.svh_nr - 1].sv_type;
+       else
+               scrub_type = XFS_SCRUB_TYPE_PROBE;
+       sc = &xfrog_scrubbers[scrub_type];
+
+       switch (sc->group) {
+       case XFROG_SCRUB_GROUP_AGHEADER:
+       case XFROG_SCRUB_GROUP_PERAG:
+               return snprintf(buf, buflen, _("AG %u %s"), vhead->svh_agno,
+                               _(sc->descr));
+       case XFROG_SCRUB_GROUP_INODE:
+               return scrub_render_ino_descr(ctx, buf, buflen,
+                               vhead->svh_ino, vhead->svh_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;
+}
+
 /* Format a scrub description. */
 int
 format_scrub_descr(
@@ -80,51 +117,6 @@ scrub_warn_incomplete_scrub(
                                _("Cross-referencing failed."));
 }
 
-/* Do a read-only check of some metadata. */
-static int
-xfs_check_metadata(
-       struct scrub_ctx                *ctx,
-       struct xfs_fd                   *xfdp,
-       unsigned int                    scrub_type,
-       struct scrub_item               *sri)
-{
-       DEFINE_DESCR(dsc, ctx, format_scrub_descr);
-       struct xfs_scrub_metadata       meta = { };
-       struct xfs_scrub_vec            vec;
-       enum xfrog_scrub_group          group;
-
-       background_sleep();
-
-       group = xfrog_scrubbers[scrub_type].group;
-       meta.sm_type = scrub_type;
-       switch (group) {
-       case XFROG_SCRUB_GROUP_AGHEADER:
-       case XFROG_SCRUB_GROUP_PERAG:
-               meta.sm_agno = sri->sri_agno;
-               break;
-       case XFROG_SCRUB_GROUP_FS:
-       case XFROG_SCRUB_GROUP_SUMMARY:
-       case XFROG_SCRUB_GROUP_ISCAN:
-       case XFROG_SCRUB_GROUP_NONE:
-               break;
-       case XFROG_SCRUB_GROUP_INODE:
-               meta.sm_ino = sri->sri_ino;
-               meta.sm_gen = sri->sri_gen;
-               break;
-       }
-
-       assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL"));
-       assert(scrub_type < XFS_SCRUB_TYPE_NR);
-       descr_set(&dsc, &meta);
-
-       dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags);
-
-       vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta);
-       vec.sv_type = scrub_type;
-       vec.sv_flags = meta.sm_flags;
-       return scrub_epilogue(ctx, &dsc, sri, &vec);
-}
-
 /*
  * Update all internal state after a scrub ioctl call.
  * Returns 0 for success, or ECANCELED to abort the program.
@@ -256,6 +248,87 @@ _("Optimization is possible."));
        return 0;
 }
 
+/* Fill out the scrub vector header from a scrub item. */
+void
+xfrog_scrubv_from_item(
+       struct xfrog_scrubv             *scrubv,
+       const struct scrub_item         *sri)
+{
+       xfrog_scrubv_init(scrubv);
+
+       if (bg_mode > 1)
+               scrubv->head.svh_rest_us = bg_mode - 1;
+       if (sri->sri_agno != -1)
+               scrubv->head.svh_agno = sri->sri_agno;
+       if (sri->sri_ino != -1ULL) {
+               scrubv->head.svh_ino = sri->sri_ino;
+               scrubv->head.svh_gen = sri->sri_gen;
+       }
+}
+
+/* Add a scrubber to the scrub vector. */
+void
+xfrog_scrubv_add_item(
+       struct xfrog_scrubv             *scrubv,
+       const struct scrub_item         *sri,
+       unsigned int                    scrub_type)
+{
+       struct xfs_scrub_vec            *v;
+
+       v = xfrog_scrubv_next_vector(scrubv);
+       v->sv_type = scrub_type;
+}
+
+/* Do a read-only check of some metadata. */
+static int
+scrub_call_kernel(
+       struct scrub_ctx                *ctx,
+       struct xfs_fd                   *xfdp,
+       struct scrub_item               *sri)
+{
+       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 (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
+                       continue;
+               xfrog_scrubv_add_item(&scrubv, sri, scrub_type);
+
+               dbg_printf("check %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 = scrub_epilogue(ctx, &dsc, sri, v);
+               if (error)
+                       return error;
+
+               /*
+                * Progress is counted by the inode for inode metadata; for
+                * everything else, it's counted for each scrub call.
+                */
+               if (!(sri->sri_state[v->sv_type] & SCRUB_ITEM_NEEDSCHECK) &&
+                   sri->sri_ino == -1ULL)
+                       progress_add(1);
+       }
+
+       return 0;
+}
+
 /* Bulk-notify user about things that could be optimized. */
 void
 scrub_report_preen_triggers(
@@ -291,6 +364,37 @@ 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(
+       struct scrub_item       *sri,
+       uint8_t                 work_mask,
+       const struct scrub_item *old)
+{
+       unsigned int            scrub_type;
+       unsigned int            nr = 0;
+
+       /* If there's nothing to do, we're done. */
+       foreach_scrub_type(scrub_type) {
+               if (sri->sri_state[scrub_type] & work_mask)
+                       nr++;
+       }
+       if (!nr)
+               return false;
+
+       foreach_scrub_type(scrub_type) {
+               uint8_t         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;
+}
+
 /* Decide if we call the kernel again to finish scrub/repair activity. */
 bool
 scrub_item_call_kernel_again(
@@ -319,6 +423,29 @@ scrub_item_call_kernel_again(
        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.
+ */
+bool
+scrub_item_schedule_work(
+       struct scrub_item       *sri,
+       uint8_t                 state_flags)
+{
+       unsigned int            scrub_type;
+       unsigned int            nr = 0;
+
+       foreach_scrub_type(scrub_type) {
+               if (!(sri->sri_state[scrub_type] & state_flags))
+                       continue;
+
+               sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
+               nr++;
+       }
+
+       return nr > 0;
+}
+
 /* Run all the incomplete scans on this scrub principal. */
 int
 scrub_item_check_file(
@@ -329,8 +456,10 @@ scrub_item_check_file(
        struct xfs_fd                   xfd;
        struct scrub_item               old_sri;
        struct xfs_fd                   *xfdp = &ctx->mnt;
-       unsigned int                    scrub_type;
-       int                             error;
+       int                             error = 0;
+
+       if (!scrub_item_schedule_work(sri, SCRUB_ITEM_NEEDSCHECK))
+               return 0;
 
        /*
         * If the caller passed us a file descriptor for a scrub, use it
@@ -343,31 +472,15 @@ scrub_item_check_file(
                xfdp = &xfd;
        }
 
-       foreach_scrub_type(scrub_type) {
-               if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK))
-                       continue;
-
-               sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES;
-               do {
-                       memcpy(&old_sri, sri, sizeof(old_sri));
-                       error = xfs_check_metadata(ctx, xfdp, scrub_type, sri);
-                       if (error)
-                               return error;
-               } while (scrub_item_call_kernel_again(sri, scrub_type,
-                                       SCRUB_ITEM_NEEDSCHECK, &old_sri));
-
-               /*
-                * Progress is counted by the inode for inode metadata; for
-                * everything else, it's counted for each scrub call.
-                */
-               if (sri->sri_ino == -1ULL)
-                       progress_add(1);
-
+       do {
+               memcpy(&old_sri, sri, sizeof(old_sri));
+               error = scrub_call_kernel(ctx, xfdp, sri);
                if (error)
-                       break;
-       }
+                       return error;
+       } while (scrub_item_call_kernel_again_future(sri, SCRUB_ITEM_NEEDSCHECK,
+                               &old_sri));
 
-       return error;
+       return 0;
 }
 
 /* How many items do we have to check? */
@@ -562,3 +675,21 @@ can_force_rebuild(
        return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE,
                        XFS_SCRUB_IFLAG_REPAIR | XFS_SCRUB_IFLAG_FORCE_REBUILD);
 }
+
+void
+check_scrubv(
+       struct scrub_ctx        *ctx)
+{
+       struct xfrog_scrubv     scrubv = { };
+
+       xfrog_scrubv_init(&scrubv);
+
+       if (debug_tweak_on("XFS_SCRUB_FORCE_SINGLE"))
+               ctx->mnt.flags |= XFROG_FLAG_SCRUB_FORCE_SINGLE;
+
+       /*
+        * We set the fallback flag if calling the kernel with a zero-length
+        * vector doesn't work.
+        */
+       xfrog_scrubv_metadata(&ctx->mnt, &scrubv);
+}
index 90578108a1c8df52b3a96e8da996c1f1cb6a842f..183b89379cb4e6dc236df34a7a3f5fd984d4cdc7 100644 (file)
@@ -138,6 +138,8 @@ bool can_scrub_parent(struct scrub_ctx *ctx);
 bool can_repair(struct scrub_ctx *ctx);
 bool can_force_rebuild(struct scrub_ctx *ctx);
 
+void check_scrubv(struct scrub_ctx *ctx);
+
 int scrub_file(struct scrub_ctx *ctx, int fd, const struct xfs_bulkstat *bstat,
                unsigned int type, struct scrub_item *sri);
 
index 98a9238f2aac3f4a78dfcce3900de7f964feef3e..bf53ee5af2cf6162a47d4593074d87c5b0dd20ae 100644 (file)
@@ -8,9 +8,24 @@
 
 /* Shared code between scrub.c and repair.c. */
 
+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);
 
+struct scrubv_descr {
+       struct xfrog_scrubv     *scrubv;
+       int                     idx;
+};
+
+#define SCRUBV_DESCR(sv)       { .scrubv = (sv), .idx = -1 }
+
+int format_scrubv_descr(struct scrub_ctx *ctx, char *buf, size_t buflen,
+               void *where);
+
 /* Predicates for scrub flag state. */
 
 static inline bool is_corrupt(const struct xfs_scrub_vec *sv)
@@ -104,5 +119,6 @@ scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type)
 bool scrub_item_call_kernel_again(struct scrub_item *sri,
                unsigned int scrub_type, uint8_t work_mask,
                const struct scrub_item *old);
+bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags);
 
 #endif /* XFS_SCRUB_SCRUB_PRIVATE_H_ */
index bb316f73e02c102fc22993e38c974f28a02666e4..f5b58de128123d1c4e2802ceac2632be69ebd138 100644 (file)
  * XFS_SCRUB_THREADS           -- start exactly this number of threads
  * XFS_SCRUB_DISK_ERROR_INTERVAL-- simulate a disk error every this many bytes
  * XFS_SCRUB_DISK_VERIFY_SKIP  -- pretend disk verify read calls succeeded
+ * XFS_SCRUB_FORCE_SINGLE      -- fall back to ioctl-per-item scrubbing
  *
  * Available even in non-debug mode:
  * SERVICE_MODE                        -- compress all error codes to 1 for LSB