Skip to content

Commit bdf8079

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`. Signed-off-by: Mariusz Zaborski <[email protected]>
1 parent 3a31abb commit bdf8079

File tree

16 files changed

+550
-68
lines changed

16 files changed

+550
-68
lines changed

cmd/zpool/zpool_main.c

Lines changed: 95 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ 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> [device]\n"));
462462
case HELP_CREATE:
463463
return (gettext("\tcreate [-fnd] [-o property=value] ... \n"
464464
"\t [-O file-system-property=value] ... \n"
@@ -513,7 +513,7 @@ get_usage(zpool_help_t idx)
513513
return (gettext("\tinitialize [-c | -s | -u] [-w] <-a | <pool> "
514514
"[<device> ...]>\n"));
515515
case HELP_SCRUB:
516-
return (gettext("\tscrub [-e | -s | -p | -C | -E | -S] [-w] "
516+
return (gettext("\tscrub [-e | -s | -p | -C | -E | -S | -R] [-w] "
517517
"<-a | <pool> [<pool> ...]>\n"));
518518
case HELP_RESILVER:
519519
return (gettext("\tresilver <pool> ...\n"));
@@ -8155,8 +8155,74 @@ zpool_do_offline(int argc, char **argv)
81558155
return (ret);
81568156
}
81578157

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

81828249
/* check options */
8183-
while ((c = getopt_long(argc, argv, "FnX", long_options,
8250+
while ((c = getopt_long(argc, argv, "FnsX", long_options,
81848251
NULL)) != -1) {
81858252
switch (c) {
81868253
case 'F':
@@ -8189,6 +8256,9 @@ zpool_do_clear(int argc, char **argv)
81898256
case 'n':
81908257
dryrun = B_TRUE;
81918258
break;
8259+
case 's':
8260+
scrub = B_TRUE;
8261+
break;
81928262
case 'X':
81938263
xtreme_rewind = B_TRUE;
81948264
break;
@@ -8256,6 +8326,14 @@ zpool_do_clear(int argc, char **argv)
82568326
if (zpool_clear(zhp, device, policy) != 0)
82578327
ret = 1;
82588328

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

82618339
nvlist_free(policy);
@@ -8357,66 +8435,6 @@ zpool_do_reopen(int argc, char **argv)
83578435
return (ret);
83588436
}
83598437

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-
84208438
static int
84218439
wait_callback(zpool_handle_t *zhp, void *data)
84228440
{
@@ -8452,14 +8470,15 @@ struct zpool_scrub_option {
84528470
};
84538471

84548472
/*
8455-
* zpool scrub [-e | -s | -p | -C | -E | -S] [-w] [-a | <pool> ...]
8473+
* zpool scrub [-e | -s | -p | -C | -E | -S | -R] [-w] [-a | <pool> ...]
84568474
*
84578475
* -a Scrub all pools.
84588476
* -e Only scrub blocks in the error log.
84598477
* -E End date of scrub.
84608478
* -S Start date of scrub.
84618479
* -s Stop. Stops any in-progress scrub.
84628480
* -p Pause. Pause in-progress scrub.
8481+
* -R Scrub only recent data.
84638482
* -w Wait. Blocks until scrub has completed.
84648483
* -C Scrub from last saved txg.
84658484
*/
@@ -8478,11 +8497,12 @@ zpool_do_scrub(int argc, char **argv)
84788497
struct zpool_scrub_option is_error_scrub = {'e', B_FALSE};
84798498
struct zpool_scrub_option is_pause = {'p', B_FALSE};
84808499
struct zpool_scrub_option is_stop = {'s', B_FALSE};
8500+
struct zpool_scrub_option is_recent = {'R', B_FALSE};
84818501
struct zpool_scrub_option is_txg_continue = {'C', B_FALSE};
84828502
struct zpool_scrub_option scrub_all = {'a', B_FALSE};
84838503

84848504
/* check options */
8485-
while ((c = getopt(argc, argv, "aspweCE:S:")) != -1) {
8505+
while ((c = getopt(argc, argv, "aspweCE:S:R")) != -1) {
84868506
switch (c) {
84878507
case 'a':
84888508
scrub_all.enabled = B_TRUE;
@@ -8506,6 +8526,9 @@ zpool_do_scrub(int argc, char **argv)
85068526
case 'p':
85078527
is_pause.enabled = B_TRUE;
85088528
break;
8529+
case 'R':
8530+
is_recent.enabled = B_TRUE;
8531+
break;
85098532
case 'w':
85108533
wait.enabled = B_TRUE;
85118534
break;
@@ -8526,9 +8549,13 @@ zpool_do_scrub(int argc, char **argv)
85268549
{&is_stop, &is_pause},
85278550
{&is_stop, &is_txg_continue},
85288551
{&is_stop, &is_error_scrub},
8552+
{&is_stop, &is_recent},
85298553
{&is_pause, &is_txg_continue},
85308554
{&is_pause, &is_error_scrub},
8555+
{&is_pause, &is_recent},
85318556
{&is_error_scrub, &is_txg_continue},
8557+
{&is_error_scrub, &is_recent},
8558+
{&is_recent, &is_txg_continue},
85328559
};
85338560

85348561
for (int i = 0; i < sizeof(scrub_exclusive_options) /
@@ -8553,6 +8580,8 @@ zpool_do_scrub(int argc, char **argv)
85538580
cb.cb_type = POOL_SCAN_NONE;
85548581
} else if (is_txg_continue.enabled) {
85558582
cb.cb_scrub_cmd = POOL_SCRUB_FROM_LAST_TXG;
8583+
} else if (is_recent.enabled) {
8584+
cb.cb_scrub_cmd = POOL_SCRUB_RECENT;
85568585
} else {
85578586
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
85588587
}

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

man/man4/zfs.4

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,6 +1967,24 @@ simply doing a metadata crawl of the pool instead.
19671967
.It Sy zfs_no_scrub_prefetch Ns = Ns Sy 0 Ns | Ns 1 Pq int
19681968
Set to disable block prefetching for scrubs.
19691969
.
1970+
.It Sy zfs_scrub_recent_time Ns = Ns Sy 14400 Pq uint
1971+
.It Sy zfs_scrub_recent_time_hours Ns = Ns Sy 4 Pq uint
1972+
.It Sy zfs_scrub_recent_time_days Ns = Ns Sy 0 Pq uint
1973+
Set the time interval that defines which data is considered most recent.
1974+
The interval is measured relative to the last known TXG in the TXG database.
1975+
.Pp
1976+
This setting can be specified using one of three properties: seconds, hours,
1977+
or days.
1978+
These properties are provided for user convenience.
1979+
Each represents the same value, expressed in different units of time.
1980+
For example, setting the
1981+
.Sy zfs_scrub_recent_time_hours
1982+
property to 1 will automatically set the
1983+
.Sy zfs_scrub_recent_time
1984+
to 3600 and
1985+
.Sy zfs_scrub_recent_time_days
1986+
to 0.
1987+
.
19701988
.It Sy zfs_nocacheflush Ns = Ns Sy 0 Ns | Ns 1 Pq int
19711989
Disable cache flush operations on disks when writing.
19721990
Setting this will cause pool corruption on power loss

0 commit comments

Comments
 (0)