diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 3730541da86f..194a0072e001 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -512,7 +512,7 @@ get_usage(zpool_help_t idx) return (gettext("\tinitialize [-c | -s | -u] [-w] " "[ ...]\n")); case HELP_SCRUB: - return (gettext("\tscrub [-e | -s | -p | -C] [-w] " + return (gettext("\tscrub [-e | -s | -p | -C | -R] [-w] " " ...\n")); case HELP_RESILVER: return (gettext("\tresilver ...\n")); @@ -8413,13 +8413,14 @@ wait_callback(zpool_handle_t *zhp, void *data) } /* - * zpool scrub [-e | -s | -p | -C] [-w] ... + * zpool scrub [-e | -s | -p | -C | -R] [-w] ... * * -e Only scrub blocks in the error log. * -s Stop. Stops any in-progress scrub. * -p Pause. Pause in-progress scrub. * -w Wait. Blocks until scrub has completed. * -C Scrub from last saved txg. + * -R Scrub only recent data. */ int zpool_do_scrub(int argc, char **argv) @@ -8436,9 +8437,10 @@ zpool_do_scrub(int argc, char **argv) boolean_t is_pause = B_FALSE; boolean_t is_stop = B_FALSE; boolean_t is_txg_continue = B_FALSE; + boolean_t is_recent_scrub = B_FALSE; /* check options */ - while ((c = getopt(argc, argv, "spweC")) != -1) { + while ((c = getopt(argc, argv, "spweCR")) != -1) { switch (c) { case 'e': is_error_scrub = B_TRUE; @@ -8455,6 +8457,9 @@ zpool_do_scrub(int argc, char **argv) case 'C': is_txg_continue = B_TRUE; break; + case 'R': + is_recent_scrub = B_TRUE; + break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); @@ -8478,11 +8483,30 @@ zpool_do_scrub(int argc, char **argv) (void) fprintf(stderr, gettext("invalid option " "combination :-e and -C are mutually exclusive\n")); usage(B_FALSE); + } else if (is_recent_scrub && is_txg_continue) { + (void) fprintf(stderr, gettext("invalid option " + "combination :-R and -C are mutually exclusive\n")); + usage(B_FALSE); + } else if (is_pause && is_recent_scrub) { + (void) fprintf(stderr, gettext("invalid option " + "combination :-p and -R are mutually exclusive\n")); + usage(B_FALSE); + } else if (is_stop && is_recent_scrub) { + (void) fprintf(stderr, gettext("invalid option " + "combination :-s and -R are mutually exclusive\n")); + usage(B_FALSE); + } else if (is_error_scrub && is_recent_scrub) { + (void) fprintf(stderr, gettext("invalid option " + "combination :-e and -R are mutually exclusive\n")); + usage(B_FALSE); } else { - if (is_error_scrub) + if (is_error_scrub) { cb.cb_type = POOL_SCAN_ERRORSCRUB; + } - if (is_pause) { + if (is_recent_scrub) { + cb.cb_scrub_cmd = POOL_SCRUB_RECENT_TXGS; + } else if (is_pause) { cb.cb_scrub_cmd = POOL_SCRUB_PAUSE; } else if (is_stop) { cb.cb_type = POOL_SCAN_NONE; diff --git a/include/sys/dsl_scan.h b/include/sys/dsl_scan.h index d9f2c1be8bc1..5cc99ad9d159 100644 --- a/include/sys/dsl_scan.h +++ b/include/sys/dsl_scan.h @@ -44,6 +44,7 @@ struct dsl_pool; struct dmu_tx; extern int zfs_scan_suspend_progress; +extern uint_t zfs_scrub_recent_txgs; /* * All members of this structure must be uint64_t, for byteswap diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index dc474e3739f3..b68f574258b8 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -1090,6 +1090,7 @@ typedef enum pool_scrub_cmd { POOL_SCRUB_NORMAL = 0, POOL_SCRUB_PAUSE, POOL_SCRUB_FROM_LAST_TXG, + POOL_SCRUB_RECENT_TXGS, POOL_SCRUB_FLAGS_END } pool_scrub_cmd_t; diff --git a/man/man4/zfs.4 b/man/man4/zfs.4 index c9f6ed0dece3..a1e851392e6b 100644 --- a/man/man4/zfs.4 +++ b/man/man4/zfs.4 @@ -2044,6 +2044,9 @@ working on a scrub between TXG flushes. .It Sy zfs_scrub_error_blocks_per_txg Ns = Ns Sy 4096 Pq uint Error blocks to be scrubbed in one txg. . +.It Sy zfs_scrub_recent_txgs Ns = Ns Sy 256 Pq uint +Number of txgs to be considered as recent when performing a recent data scrub. +. .It Sy zfs_scan_checkpoint_intval Ns = Ns Sy 7200 Ns s Po 2 hour Pc Pq uint To preserve progress across reboots, the sequential scan algorithm periodically needs to stop metadata scanning and issue all the verification I/O to disk. diff --git a/man/man8/zpool-scrub.8 b/man/man8/zpool-scrub.8 index 761a51e75aa4..b9304949d424 100644 --- a/man/man8/zpool-scrub.8 +++ b/man/man8/zpool-scrub.8 @@ -36,7 +36,7 @@ .Sh SYNOPSIS .Nm zpool .Cm scrub -.Op Ns Fl e | Ns Fl p | Fl s Ns | Fl C Ns +.Op Ns Fl e | Ns Fl p | Fl s Ns | Fl C Ns | Fl R Ns .Op Fl w .Ar pool Ns … . @@ -117,6 +117,10 @@ resilvering, nor can it be run when a regular scrub is paused. Continue scrub from last saved txg (see zpool .Sy last_scrubbed_txg property). +.It Fl R +Scrub only recent data (this can be controlled by the +.Sy zfs_scrub_recent_txgs +parameter). .El .Sh EXAMPLES .Ss Example 1 diff --git a/module/zfs/dsl_scan.c b/module/zfs/dsl_scan.c index 60768e72799d..b7938b874ff0 100644 --- a/module/zfs/dsl_scan.c +++ b/module/zfs/dsl_scan.c @@ -242,6 +242,9 @@ static int zfs_free_bpobj_enabled = 1; /* Error blocks to be scrubbed in one txg. */ static uint_t zfs_scrub_error_blocks_per_txg = 1 << 12; +/* The number of TXGs should be scrubbed while scrubbing recent data. */ +uint_t zfs_scrub_recent_txgs = 256; + /* the order has to match pool_scan_type */ static scan_cb_t *scan_funcs[POOL_SCAN_FUNCS] = { NULL, @@ -5349,4 +5352,7 @@ ZFS_MODULE_PARAM(zfs, zfs_, resilver_defer_percent, UINT, ZMOD_RW, ZFS_MODULE_PARAM(zfs, zfs_, scrub_error_blocks_per_txg, UINT, ZMOD_RW, "Error blocks to be scrubbed in one txg"); + +ZFS_MODULE_PARAM(zfs, zfs_, scrub_recent_txgs, UINT, ZMOD_RW, + "The number of TXGs should be scrubbed while scrubbing recent data"); /* END CSTYLED */ diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index b1b0ae54460b..70eda311cd8e 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -1721,6 +1721,16 @@ zfs_ioc_pool_scrub(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) } else if (scan_cmd == POOL_SCRUB_FROM_LAST_TXG) { error = spa_scan_range(spa, scan_type, spa_get_last_scrubbed_txg(spa), 0); + } else if (scan_cmd == POOL_SCRUB_RECENT_TXGS) { + uint64_t start; + + start = 0; + if (spa_last_synced_txg(spa) > zfs_scrub_recent_txgs) { + start = spa_last_synced_txg(spa) - + zfs_scrub_recent_txgs; + } + + error = spa_scan_range(spa, scan_type, start, 0); } else { error = spa_scan(spa, scan_type); }