From: Darrick J. Wong <djwong@xxxxxxxxxx> 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@xxxxxxxxxx> --- scrub/phase5.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++------ scrub/scrub.c | 18 +++---- scrub/scrub.h | 1 3 files changed, 145 insertions(+), 24 deletions(-) diff --git a/scrub/phase5.c b/scrub/phase5.c index 0a91e4f0640..b4c635d3452 100644 --- a/scrub/phase5.c +++ b/scrub/phase5.c @@ -384,26 +384,146 @@ check_fs_label( return error; } -/* Check directory connectivity. */ -int -phase5_func( - struct scrub_ctx *ctx) -{ +typedef int (*fs_scan_item_fn)(struct scrub_ctx *, struct action_list *); + +struct fs_scan_item { struct action_list alist; - bool aborted = false; + 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; /* - * 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. + * Delay each successive fs scan by a second so that the threads are + * less likely to contend on the inobt and inode buffers. */ - action_list_init(&alist); - ret = scrub_iscan_metadata(ctx, &alist); - if (ret) - return ret; - ret = action_list_process(ctx, ctx->mnt.fd, &alist, + 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) +{ + bool aborted = false; + int ret; + + /* + * 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. + */ + 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; } diff --git a/scrub/scrub.c b/scrub/scrub.c index a22633a8115..b7ec54c16a4 100644 --- a/scrub/scrub.c +++ b/scrub/scrub.c @@ -422,15 +422,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( @@ -449,6 +440,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( diff --git a/scrub/scrub.h b/scrub/scrub.h index 927f86de9ec..5e3f40bf1f4 100644 --- a/scrub/scrub.h +++ b/scrub/scrub.h @@ -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);