Skip to content

Commit d4de3ca

Browse files
committed
scrub: add option to scrub only recent data
Recent data is defined as the last known point in the TXG database, minus a user-defined time interval (default: 4h). This feature can be triggered using either of the following commands: `zpool clean -s` or `zpool scrub -R`. Sponsored-By: Wasabi Technology, Inc. Sponsored-By: Klara Inc. Signed-off-by: Mariusz Zaborski <[email protected]>
1 parent db6742d commit d4de3ca

File tree

19 files changed

+586
-408
lines changed

19 files changed

+586
-408
lines changed

cmd/zpool/zpool_main.c

Lines changed: 97 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,8 @@ get_usage(zpool_help_t idx)
458458
return (gettext("\tattach [-fsw] [-o property=value] "
459459
"<pool> <device> <new-device>\n"));
460460
case HELP_CLEAR:
461-
return (gettext("\tclear [[--power]|[-nF]] <pool> [device]\n"));
461+
return (gettext("\tclear [[--power]|[-nsF]] <pool> "
462+
"[device]\n"));
462463
case HELP_CREATE:
463464
return (gettext("\tcreate [-fnd] [-o property=value] ... \n"
464465
"\t [-O file-system-property=value] ... \n"
@@ -513,8 +514,8 @@ get_usage(zpool_help_t idx)
513514
return (gettext("\tinitialize [-c | -s | -u] [-w] <-a | <pool> "
514515
"[<device> ...]>\n"));
515516
case HELP_SCRUB:
516-
return (gettext("\tscrub [-e | -s | -p | -C | -E | -S] [-w] "
517-
"<-a | <pool> [<pool> ...]>\n"));
517+
return (gettext("\tscrub [-e | -s | -p | -C | -E | -S | -R] "
518+
"[-w] <-a | <pool> [<pool> ...]>\n"));
518519
case HELP_RESILVER:
519520
return (gettext("\tresilver <pool> ...\n"));
520521
case HELP_TRIM:
@@ -8155,8 +8156,74 @@ zpool_do_offline(int argc, char **argv)
81558156
return (ret);
81568157
}
81578158

8159+
typedef struct scrub_cbdata {
8160+
int cb_type;
8161+
pool_scrub_cmd_t cb_scrub_cmd;
8162+
time_t cb_date_start;
8163+
time_t cb_date_end;
8164+
} scrub_cbdata_t;
8165+
8166+
static boolean_t
8167+
zpool_has_checkpoint(zpool_handle_t *zhp)
8168+
{
8169+
nvlist_t *config, *nvroot;
8170+
8171+
config = zpool_get_config(zhp, NULL);
8172+
8173+
if (config != NULL) {
8174+
pool_checkpoint_stat_t *pcs = NULL;
8175+
uint_t c;
8176+
8177+
nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
8178+
(void) nvlist_lookup_uint64_array(nvroot,
8179+
ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c);
8180+
8181+
if (pcs == NULL || pcs->pcs_state == CS_NONE)
8182+
return (B_FALSE);
8183+
8184+
assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS ||
8185+
pcs->pcs_state == CS_CHECKPOINT_DISCARDING);
8186+
return (B_TRUE);
8187+
}
8188+
8189+
return (B_FALSE);
8190+
}
8191+
8192+
static int
8193+
zpool_scrub(zpool_handle_t *zhp, scrub_cbdata_t *cb)
8194+
{
8195+
int err;
8196+
8197+
/*
8198+
* Ignore faulted pools.
8199+
*/
8200+
if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
8201+
(void) fprintf(stderr, gettext("cannot scan '%s': pool is "
8202+
"currently unavailable\n"), zpool_get_name(zhp));
8203+
return (1);
8204+
}
8205+
8206+
err = zpool_scan_range(zhp, cb->cb_type, cb->cb_scrub_cmd,
8207+
cb->cb_date_start, cb->cb_date_end);
8208+
if (err == 0 && zpool_has_checkpoint(zhp) &&
8209+
cb->cb_type == POOL_SCAN_SCRUB) {
8210+
(void) printf(gettext("warning: will not scrub state that "
8211+
"belongs to the checkpoint of pool '%s'\n"),
8212+
zpool_get_name(zhp));
8213+
}
8214+
8215+
return (err != 0);
8216+
}
8217+
8218+
static int
8219+
scrub_callback(zpool_handle_t *zhp, void *data)
8220+
{
8221+
scrub_cbdata_t *cb = data;
8222+
return (zpool_scrub(zhp, cb));
8223+
}
8224+
81588225
/*
8159-
* zpool clear [-nF]|[--power] <pool> [device]
8226+
* zpool clear [-nsF]|[--power] <pool> [device]
81608227
*
81618228
* Clear all errors associated with a pool or a particular device.
81628229
*/
@@ -8169,6 +8236,7 @@ zpool_do_clear(int argc, char **argv)
81698236
boolean_t do_rewind = B_FALSE;
81708237
boolean_t xtreme_rewind = B_FALSE;
81718238
boolean_t is_power_on = B_FALSE;
8239+
boolean_t scrub = B_FALSE;
81728240
uint32_t rewind_policy = ZPOOL_NO_REWIND;
81738241
nvlist_t *policy = NULL;
81748242
zpool_handle_t *zhp;
@@ -8180,7 +8248,7 @@ zpool_do_clear(int argc, char **argv)
81808248
};
81818249

81828250
/* check options */
8183-
while ((c = getopt_long(argc, argv, "FnX", long_options,
8251+
while ((c = getopt_long(argc, argv, "FnsX", long_options,
81848252
NULL)) != -1) {
81858253
switch (c) {
81868254
case 'F':
@@ -8189,6 +8257,9 @@ zpool_do_clear(int argc, char **argv)
81898257
case 'n':
81908258
dryrun = B_TRUE;
81918259
break;
8260+
case 's':
8261+
scrub = B_TRUE;
8262+
break;
81928263
case 'X':
81938264
xtreme_rewind = B_TRUE;
81948265
break;
@@ -8256,6 +8327,14 @@ zpool_do_clear(int argc, char **argv)
82568327
if (zpool_clear(zhp, device, policy) != 0)
82578328
ret = 1;
82588329

8330+
if (ret == 0 && !dryrun && scrub) {
8331+
scrub_cbdata_t cbdata = {
8332+
.cb_type = POOL_SCAN_SCRUB,
8333+
.cb_scrub_cmd = POOL_SCRUB_RECENT,
8334+
};
8335+
ret = scrub_callback(zhp, &cbdata);
8336+
}
8337+
82598338
zpool_close(zhp);
82608339

82618340
nvlist_free(policy);
@@ -8357,66 +8436,6 @@ zpool_do_reopen(int argc, char **argv)
83578436
return (ret);
83588437
}
83598438

8360-
typedef struct scrub_cbdata {
8361-
int cb_type;
8362-
pool_scrub_cmd_t cb_scrub_cmd;
8363-
time_t cb_date_start;
8364-
time_t cb_date_end;
8365-
} scrub_cbdata_t;
8366-
8367-
static boolean_t
8368-
zpool_has_checkpoint(zpool_handle_t *zhp)
8369-
{
8370-
nvlist_t *config, *nvroot;
8371-
8372-
config = zpool_get_config(zhp, NULL);
8373-
8374-
if (config != NULL) {
8375-
pool_checkpoint_stat_t *pcs = NULL;
8376-
uint_t c;
8377-
8378-
nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
8379-
(void) nvlist_lookup_uint64_array(nvroot,
8380-
ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c);
8381-
8382-
if (pcs == NULL || pcs->pcs_state == CS_NONE)
8383-
return (B_FALSE);
8384-
8385-
assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS ||
8386-
pcs->pcs_state == CS_CHECKPOINT_DISCARDING);
8387-
return (B_TRUE);
8388-
}
8389-
8390-
return (B_FALSE);
8391-
}
8392-
8393-
static int
8394-
scrub_callback(zpool_handle_t *zhp, void *data)
8395-
{
8396-
scrub_cbdata_t *cb = data;
8397-
int err;
8398-
8399-
/*
8400-
* Ignore faulted pools.
8401-
*/
8402-
if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
8403-
(void) fprintf(stderr, gettext("cannot scan '%s': pool is "
8404-
"currently unavailable\n"), zpool_get_name(zhp));
8405-
return (1);
8406-
}
8407-
8408-
err = zpool_scan_range(zhp, cb->cb_type, cb->cb_scrub_cmd,
8409-
cb->cb_date_start, cb->cb_date_end);
8410-
if (err == 0 && zpool_has_checkpoint(zhp) &&
8411-
cb->cb_type == POOL_SCAN_SCRUB) {
8412-
(void) printf(gettext("warning: will not scrub state that "
8413-
"belongs to the checkpoint of pool '%s'\n"),
8414-
zpool_get_name(zhp));
8415-
}
8416-
8417-
return (err != 0);
8418-
}
8419-
84208439
static int
84218440
wait_callback(zpool_handle_t *zhp, void *data)
84228441
{
@@ -8452,14 +8471,15 @@ struct zpool_scrub_option {
84528471
};
84538472

84548473
/*
8455-
* zpool scrub [-e | -s | -p | -C | -E | -S] [-w] [-a | <pool> ...]
8474+
* zpool scrub [-e | -s | -p | -C | -E | -S | -R] [-w] [-a | <pool> ...]
84568475
*
84578476
* -a Scrub all pools.
84588477
* -e Only scrub blocks in the error log.
84598478
* -E End date of scrub.
84608479
* -S Start date of scrub.
84618480
* -s Stop. Stops any in-progress scrub.
84628481
* -p Pause. Pause in-progress scrub.
8482+
* -R Scrub only recent data.
84638483
* -w Wait. Blocks until scrub has completed.
84648484
* -C Scrub from last saved txg.
84658485
*/
@@ -8478,11 +8498,12 @@ zpool_do_scrub(int argc, char **argv)
84788498
struct zpool_scrub_option is_error_scrub = {'e', B_FALSE};
84798499
struct zpool_scrub_option is_pause = {'p', B_FALSE};
84808500
struct zpool_scrub_option is_stop = {'s', B_FALSE};
8501+
struct zpool_scrub_option is_recent = {'R', B_FALSE};
84818502
struct zpool_scrub_option is_txg_continue = {'C', B_FALSE};
84828503
struct zpool_scrub_option scrub_all = {'a', B_FALSE};
84838504

84848505
/* check options */
8485-
while ((c = getopt(argc, argv, "aspweCE:S:")) != -1) {
8506+
while ((c = getopt(argc, argv, "aspweCE:S:R")) != -1) {
84868507
switch (c) {
84878508
case 'a':
84888509
scrub_all.enabled = B_TRUE;
@@ -8506,6 +8527,9 @@ zpool_do_scrub(int argc, char **argv)
85068527
case 'p':
85078528
is_pause.enabled = B_TRUE;
85088529
break;
8530+
case 'R':
8531+
is_recent.enabled = B_TRUE;
8532+
break;
85098533
case 'w':
85108534
wait.enabled = B_TRUE;
85118535
break;
@@ -8526,9 +8550,13 @@ zpool_do_scrub(int argc, char **argv)
85268550
{&is_stop, &is_pause},
85278551
{&is_stop, &is_txg_continue},
85288552
{&is_stop, &is_error_scrub},
8553+
{&is_stop, &is_recent},
85298554
{&is_pause, &is_txg_continue},
85308555
{&is_pause, &is_error_scrub},
8556+
{&is_pause, &is_recent},
85318557
{&is_error_scrub, &is_txg_continue},
8558+
{&is_error_scrub, &is_recent},
8559+
{&is_recent, &is_txg_continue},
85328560
};
85338561

85348562
for (int i = 0; i < sizeof (scrub_exclusive_options) /
@@ -8553,6 +8581,8 @@ zpool_do_scrub(int argc, char **argv)
85538581
cb.cb_type = POOL_SCAN_NONE;
85548582
} else if (is_txg_continue.enabled) {
85558583
cb.cb_scrub_cmd = POOL_SCRUB_FROM_LAST_TXG;
8584+
} else if (is_recent.enabled) {
8585+
cb.cb_scrub_cmd = POOL_SCRUB_RECENT;
85568586
} else {
85578587
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
85588588
}

include/os/freebsd/spl/sys/mod.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@
8383
#define param_set_deadman_ziotime_args(var) \
8484
CTLTYPE_U64, NULL, 0, param_set_deadman_ziotime, "QU"
8585

86+
#define scrub_param_set_recent_time_args(var) \
87+
CTLTYPE_U64, NULL, 0, scrub_param_set_recent_time, "QU"
88+
89+
#define scrub_param_set_recent_time_hours_args(var) \
90+
CTLTYPE_U64, NULL, 0, scrub_param_set_recent_time_hours, "QU"
91+
92+
#define scrub_param_set_recent_time_days_args(var) \
93+
CTLTYPE_U64, NULL, 0, scrub_param_set_recent_time_days, "QU"
94+
8695
#define param_set_multihost_interval_args(var) \
8796
CTLTYPE_U64, NULL, 0, param_set_multihost_interval, "QU"
8897

include/sys/dsl_scan.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ struct dsl_pool;
4545
struct dmu_tx;
4646

4747
extern int zfs_scan_suspend_progress;
48+
extern uint64_t zfs_scrub_recent_time;
49+
extern uint64_t zfs_scrub_recent_time_hours;
50+
extern uint64_t zfs_scrub_recent_time_days;
4851

4952
/*
5053
* All members of this structure must be uint64_t, for byteswap
@@ -222,6 +225,10 @@ void dsl_scan_freed(spa_t *spa, const blkptr_t *bp);
222225
void dsl_scan_io_queue_destroy(dsl_scan_io_queue_t *queue);
223226
void dsl_scan_io_queue_vdev_xfer(vdev_t *svd, vdev_t *tvd);
224227

228+
int scrub_param_set_recent_time(ZFS_MODULE_PARAM_ARGS);
229+
int scrub_param_set_recent_time_hours(ZFS_MODULE_PARAM_ARGS);
230+
int scrub_param_set_recent_time_days(ZFS_MODULE_PARAM_ARGS);
231+
225232
#ifdef __cplusplus
226233
}
227234
#endif

include/sys/fs/zfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,7 @@ typedef enum pool_scrub_cmd {
10981098
POOL_SCRUB_NORMAL = 0,
10991099
POOL_SCRUB_PAUSE,
11001100
POOL_SCRUB_FROM_LAST_TXG,
1101+
POOL_SCRUB_RECENT,
11011102
POOL_SCRUB_FLAGS_END
11021103
} pool_scrub_cmd_t;
11031104

include/zfs_crrd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,6 @@ void rrd_add(rrd_t *rrd, hrtime_t time, uint64_t txg);
7171

7272
void dbrrd_add(dbrrd_t *db, hrtime_t time, uint64_t txg);
7373
uint64_t dbrrd_query(dbrrd_t *r, hrtime_t tv, dbrrd_rounding_t rouding);
74+
hrtime_t dbrrd_tail_time(dbrrd_t *r);
7475

7576
#endif

0 commit comments

Comments
 (0)