From: Darrick J. Wong <djwong@xxxxxxxxxx> Newer flash storage devices aren't as bad as the old ones when it comes to trimming unused storage. We know that the first block of each AG is always used, and therefore each AG can be trimmed independently. Therefore, do them all in parallel. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- scrub/phase8.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++----- scrub/vfs.c | 10 +++++-- scrub/vfs.h | 2 + 3 files changed, 81 insertions(+), 11 deletions(-) diff --git a/scrub/phase8.c b/scrub/phase8.c index ac667fc91fb..a8ea8db706b 100644 --- a/scrub/phase8.c +++ b/scrub/phase8.c @@ -18,6 +18,7 @@ #include "repair.h" #include "vfs.h" #include "atomic.h" +#include "disk.h" /* Phase 8: Trim filesystem. */ @@ -45,24 +46,89 @@ fstrim_ok( return true; } +struct trim_ctl { + uint64_t datadev_end_pos; + bool aborted; +}; + +/* Trim each AG. */ +static void +trim_ag( + struct workqueue *wq, + xfs_agnumber_t agno, + void *arg) +{ + struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; + struct trim_ctl *tctl = arg; + uint64_t pos, len, eoag_pos; + int error; + + pos = cvt_agbno_to_b(&ctx->mnt, agno, 0); + eoag_pos = cvt_agbno_to_b(&ctx->mnt, agno, ctx->mnt.fsgeom.agblocks); + len = min(tctl->datadev_end_pos, eoag_pos) - pos; + + error = fstrim(ctx, pos, len); + if (error) { + char descr[DESCR_BUFSZ]; + + snprintf(descr, sizeof(descr) - 1, _("fstrim agno %u"), agno); + str_liberror(ctx, error, descr); + tctl->aborted = true; + return; + } + + progress_add(1); +} + /* Trim the filesystem, if desired. */ int phase8_func( struct scrub_ctx *ctx) { - int error; + struct workqueue wq; + struct trim_ctl tctl = { + .aborted = false, + }; + xfs_agnumber_t agno; + int error, err2; if (!fstrim_ok(ctx)) return 0; - error = fstrim(ctx); + tctl.datadev_end_pos = cvt_off_fsb_to_b(&ctx->mnt, + ctx->mnt.fsgeom.datablocks); + + error = -workqueue_create(&wq, (struct xfs_mount *)ctx, + disk_heads(ctx->datadev)); if (error) { - str_liberror(ctx, error, _("fstrim")); + str_liberror(ctx, error, _("creating fstrim workqueue")); return error; } - progress_add(1); - return 0; + /* Trim each AG in parallel. */ + for (agno = 0; + agno < ctx->mnt.fsgeom.agcount && !tctl.aborted; + agno++) { + error = -workqueue_add(&wq, trim_ag, agno, &tctl); + if (error) { + str_liberror(ctx, error, + _("queueing per-AG fstrim work")); + goto out_wq; + } + } + +out_wq: + err2 = -workqueue_terminate(&wq); + if (err2) { + str_liberror(ctx, err2, _("finishing fstrim work")); + if (!error && err2) + error = err2; + } + workqueue_destroy(&wq); + + if (!error && tctl.aborted) + return ECANCELED; + return error; } /* Estimate how much work we're going to do. */ @@ -76,9 +142,9 @@ phase8_estimate( *items = 0; if (fstrim_ok(ctx)) - *items = 1; + *items = ctx->mnt.fsgeom.agcount; - *nr_threads = 1; + *nr_threads = disk_heads(ctx->datadev); *rshift = 0; return 0; } diff --git a/scrub/vfs.c b/scrub/vfs.c index ca34972d401..85ee2694b00 100644 --- a/scrub/vfs.c +++ b/scrub/vfs.c @@ -298,11 +298,15 @@ struct fstrim_range { /* Call FITRIM to trim all the unused space in a filesystem. */ int fstrim( - struct scrub_ctx *ctx) + struct scrub_ctx *ctx, + uint64_t offset, + uint64_t len) { - struct fstrim_range range = {0}; + struct fstrim_range range = { + .start = offset, + .len = len, + }; - range.len = ULLONG_MAX; if (ioctl(ctx->mnt.fd, FITRIM, &range) == 0) return 0; if (errno == EOPNOTSUPP || errno == ENOTTY) diff --git a/scrub/vfs.h b/scrub/vfs.h index 14f2a583eb1..311c403fa4e 100644 --- a/scrub/vfs.h +++ b/scrub/vfs.h @@ -24,6 +24,6 @@ typedef int (*scan_fs_tree_dirent_fn)(struct scrub_ctx *, const char *, int scan_fs_tree(struct scrub_ctx *ctx, scan_fs_tree_dir_fn dir_fn, scan_fs_tree_dirent_fn dirent_fn, void *arg); -int fstrim(struct scrub_ctx *ctx); +int fstrim(struct scrub_ctx *ctx, uint64_t offset, uint64_t len); #endif /* XFS_SCRUB_VFS_H_ */