]> git.apps.os.sepia.ceph.com Git - xfsprogs-dev.git/commitdiff
xfs_scrub: use multiple threads to run in-kernel metadata scrubs that scan inodes
authorDarrick J. Wong <djwong@kernel.org>
Mon, 22 Apr 2024 17:01:17 +0000 (10:01 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Mon, 3 Jun 2024 18:37:42 +0000 (11:37 -0700)
Instead of running the inode link count and quotacheck scanners in
serial, run them in parallel, with a slight delay to stagger the work to
reduce inode resource contention.

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

index 0a91e4f0640b3980b39cdc7a7432a20111dde898..b4c635d3452b305de0f439a80465a937eb6dfe3e 100644 (file)
@@ -384,26 +384,146 @@ out:
        return error;
 }
 
+typedef int (*fs_scan_item_fn)(struct scrub_ctx *, struct action_list *);
+
+struct fs_scan_item {
+       struct action_list      alist;
+       bool                    *abortedp;
+       fs_scan_item_fn         scrub_fn;
+};
+
+/* Run one full-fs scan scrubber in this thread. */
+static void
+fs_scan_worker(
+       struct workqueue        *wq,
+       xfs_agnumber_t          nr,
+       void                    *arg)
+{
+       struct timespec         tv;
+       struct fs_scan_item     *item = arg;
+       struct scrub_ctx        *ctx = wq->wq_ctx;
+       int                     ret;
+
+       /*
+        * Delay each successive fs scan by a second so that the threads are
+        * less likely to contend on the inobt and inode buffers.
+        */
+       if (nr) {
+               tv.tv_sec = nr;
+               tv.tv_nsec = 0;
+               nanosleep(&tv, NULL);
+       }
+
+       ret = item->scrub_fn(ctx, &item->alist);
+       if (ret) {
+               str_liberror(ctx, ret, _("checking fs scan metadata"));
+               *item->abortedp = true;
+               goto out;
+       }
+
+       ret = action_list_process(ctx, ctx->mnt.fd, &item->alist,
+                       ALP_COMPLAIN_IF_UNFIXED | ALP_NOPROGRESS);
+       if (ret) {
+               str_liberror(ctx, ret, _("repairing fs scan metadata"));
+               *item->abortedp = true;
+               goto out;
+       }
+
+out:
+       free(item);
+       return;
+}
+
+/* Queue one full-fs scan scrubber. */
+static int
+queue_fs_scan(
+       struct workqueue        *wq,
+       bool                    *abortedp,
+       xfs_agnumber_t          nr,
+       fs_scan_item_fn         scrub_fn)
+{
+       struct fs_scan_item     *item;
+       struct scrub_ctx        *ctx = wq->wq_ctx;
+       int                     ret;
+
+       item = malloc(sizeof(struct fs_scan_item));
+       if (!item) {
+               ret = ENOMEM;
+               str_liberror(ctx, ret, _("setting up fs scan"));
+               return ret;
+       }
+       action_list_init(&item->alist);
+       item->scrub_fn = scrub_fn;
+       item->abortedp = abortedp;
+
+       ret = -workqueue_add(wq, fs_scan_worker, nr, item);
+       if (ret)
+               str_liberror(ctx, ret, _("queuing fs scan work"));
+
+       return ret;
+}
+
+/* Run multiple full-fs scan scrubbers at the same time. */
+static int
+run_kernel_fs_scan_scrubbers(
+       struct scrub_ctx        *ctx)
+{
+       struct workqueue        wq_fs_scan;
+       unsigned int            nr_threads = scrub_nproc_workqueue(ctx);
+       xfs_agnumber_t          nr = 0;
+       bool                    aborted = false;
+       int                     ret, ret2;
+
+       ret = -workqueue_create(&wq_fs_scan, (struct xfs_mount *)ctx,
+                       nr_threads);
+       if (ret) {
+               str_liberror(ctx, ret, _("setting up fs scan workqueue"));
+               return ret;
+       }
+
+       /*
+        * The nlinks scanner is much faster than quotacheck because it only
+        * walks directories, so we start it first.
+        */
+       ret = queue_fs_scan(&wq_fs_scan, &aborted, nr, scrub_nlinks);
+       if (ret)
+               goto wait;
+
+       if (nr_threads > 1)
+               nr++;
+
+       ret = queue_fs_scan(&wq_fs_scan, &aborted, nr, scrub_quotacheck);
+       if (ret)
+               goto wait;
+
+wait:
+       ret2 = -workqueue_terminate(&wq_fs_scan);
+       if (ret2) {
+               str_liberror(ctx, ret2, _("joining fs scan workqueue"));
+               if (!ret)
+                       ret = ret2;
+       }
+       if (aborted && !ret)
+               ret = ECANCELED;
+
+       workqueue_destroy(&wq_fs_scan);
+       return ret;
+}
+
 /* Check directory connectivity. */
 int
 phase5_func(
        struct scrub_ctx        *ctx)
 {
-       struct action_list      alist;
        bool                    aborted = false;
        int                     ret;
 
        /*
-        * Check and fix anything that requires a full inode scan.  We do this
-        * after we've checked all inodes and repaired anything that could get
-        * in the way of a scan.
+        * Check and fix anything that requires a full filesystem scan.  We do
+        * this after we've checked all inodes and repaired anything that could
+        * get in the way of a scan.
         */
-       action_list_init(&alist);
-       ret = scrub_iscan_metadata(ctx, &alist);
-       if (ret)
-               return ret;
-       ret = action_list_process(ctx, ctx->mnt.fd, &alist,
-                       ALP_COMPLAIN_IF_UNFIXED | ALP_NOPROGRESS);
+       ret = run_kernel_fs_scan_scrubbers(ctx);
        if (ret)
                return ret;
 
@@ -436,7 +556,7 @@ phase5_estimate(
        int                     *rshift)
 {
        *items = scrub_estimate_iscan_work(ctx);
-       *nr_threads = scrub_nproc(ctx);
+       *nr_threads = scrub_nproc(ctx) * 2;
        *rshift = 0;
        return 0;
 }
index 436ccb0ca8c3cd8e8ca4f85c4a14205cb0a43f38..cf056779526e1cb0547c45d0428a7460337166db 100644 (file)
@@ -417,15 +417,6 @@ scrub_summary_metadata(
        return scrub_group(ctx, XFROG_SCRUB_GROUP_SUMMARY, 0, alist);
 }
 
-/* Scrub all metadata requiring a full inode scan. */
-int
-scrub_iscan_metadata(
-       struct scrub_ctx                *ctx,
-       struct action_list              *alist)
-{
-       return scrub_group(ctx, XFROG_SCRUB_GROUP_ISCAN, 0, alist);
-}
-
 /* Scrub /only/ the superblock summary counters. */
 int
 scrub_fs_counters(
@@ -444,6 +435,15 @@ scrub_quotacheck(
        return scrub_meta_type(ctx, XFS_SCRUB_TYPE_QUOTACHECK, 0, alist);
 }
 
+/* Scrub /only/ the file link counters. */
+int
+scrub_nlinks(
+       struct scrub_ctx                *ctx,
+       struct action_list              *alist)
+{
+       return scrub_meta_type(ctx, XFS_SCRUB_TYPE_NLINKS, 0, alist);
+}
+
 /* How many items do we have to check? */
 unsigned int
 scrub_estimate_ag_work(
index 927f86de9ec21f275f5b6168b3295e2294f9317b..5e3f40bf1f4171fa4e1870ef390546537b43bcd1 100644 (file)
@@ -28,6 +28,7 @@ int scrub_iscan_metadata(struct scrub_ctx *ctx, struct action_list *alist);
 int scrub_summary_metadata(struct scrub_ctx *ctx, struct action_list *alist);
 int scrub_fs_counters(struct scrub_ctx *ctx, struct action_list *alist);
 int scrub_quotacheck(struct scrub_ctx *ctx, struct action_list *alist);
+int scrub_nlinks(struct scrub_ctx *ctx, struct action_list *alist);
 
 bool can_scrub_fs_metadata(struct scrub_ctx *ctx);
 bool can_scrub_inode(struct scrub_ctx *ctx);