Skip to content

Commit 5859451

Browse files
oshogborobn
authored andcommitted
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 3488ecf commit 5859451

File tree

17 files changed

+557
-71
lines changed

17 files changed

+557
-71
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> <vdev> <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:
@@ -8193,8 +8194,74 @@ zpool_do_offline(int argc, char **argv)
81938194
return (ret);
81948195
}
81958196

8197+
typedef struct scrub_cbdata {
8198+
int cb_type;
8199+
pool_scrub_cmd_t cb_scrub_cmd;
8200+
time_t cb_date_start;
8201+
time_t cb_date_end;
8202+
} scrub_cbdata_t;
8203+
8204+
static boolean_t
8205+
zpool_has_checkpoint(zpool_handle_t *zhp)
8206+
{
8207+
nvlist_t *config, *nvroot;
8208+
8209+
config = zpool_get_config(zhp, NULL);
8210+
8211+
if (config != NULL) {
8212+
pool_checkpoint_stat_t *pcs = NULL;
8213+
uint_t c;
8214+
8215+
nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
8216+
(void) nvlist_lookup_uint64_array(nvroot,
8217+
ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c);
8218+
8219+
if (pcs == NULL || pcs->pcs_state == CS_NONE)
8220+
return (B_FALSE);
8221+
8222+
assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS ||
8223+
pcs->pcs_state == CS_CHECKPOINT_DISCARDING);
8224+
return (B_TRUE);
8225+
}
8226+
8227+
return (B_FALSE);
8228+
}
8229+
8230+
static int
8231+
zpool_scrub(zpool_handle_t *zhp, scrub_cbdata_t *cb)
8232+
{
8233+
int err;
8234+
8235+
/*
8236+
* Ignore faulted pools.
8237+
*/
8238+
if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
8239+
(void) fprintf(stderr, gettext("cannot scan '%s': pool is "
8240+
"currently unavailable\n"), zpool_get_name(zhp));
8241+
return (1);
8242+
}
8243+
8244+
err = zpool_scan_range(zhp, cb->cb_type, cb->cb_scrub_cmd,
8245+
cb->cb_date_start, cb->cb_date_end);
8246+
if (err == 0 && zpool_has_checkpoint(zhp) &&
8247+
cb->cb_type == POOL_SCAN_SCRUB) {
8248+
(void) printf(gettext("warning: will not scrub state that "
8249+
"belongs to the checkpoint of pool '%s'\n"),
8250+
zpool_get_name(zhp));
8251+
}
8252+
8253+
return (err != 0);
8254+
}
8255+
8256+
static int
8257+
scrub_callback(zpool_handle_t *zhp, void *data)
8258+
{
8259+
scrub_cbdata_t *cb = data;
8260+
return (zpool_scrub(zhp, cb));
8261+
}
8262+
81968263
/*
8197-
* zpool clear [-nF]|[--power] <pool> [device]
8264+
* zpool clear [-nsF]|[--power] <pool> [device]
81988265
*
81998266
* Clear all errors associated with a pool or a particular device.
82008267
*/
@@ -8207,6 +8274,7 @@ zpool_do_clear(int argc, char **argv)
82078274
boolean_t do_rewind = B_FALSE;
82088275
boolean_t xtreme_rewind = B_FALSE;
82098276
boolean_t is_power_on = B_FALSE;
8277+
boolean_t scrub = B_FALSE;
82108278
uint32_t rewind_policy = ZPOOL_NO_REWIND;
82118279
nvlist_t *policy = NULL;
82128280
zpool_handle_t *zhp;
@@ -8218,7 +8286,7 @@ zpool_do_clear(int argc, char **argv)
82188286
};
82198287

82208288
/* check options */
8221-
while ((c = getopt_long(argc, argv, "FnX", long_options,
8289+
while ((c = getopt_long(argc, argv, "FnsX", long_options,
82228290
NULL)) != -1) {
82238291
switch (c) {
82248292
case 'F':
@@ -8227,6 +8295,9 @@ zpool_do_clear(int argc, char **argv)
82278295
case 'n':
82288296
dryrun = B_TRUE;
82298297
break;
8298+
case 's':
8299+
scrub = B_TRUE;
8300+
break;
82308301
case 'X':
82318302
xtreme_rewind = B_TRUE;
82328303
break;
@@ -8294,6 +8365,14 @@ zpool_do_clear(int argc, char **argv)
82948365
if (zpool_clear(zhp, device, policy) != 0)
82958366
ret = 1;
82968367

8368+
if (ret == 0 && !dryrun && scrub) {
8369+
scrub_cbdata_t cbdata = {
8370+
.cb_type = POOL_SCAN_SCRUB,
8371+
.cb_scrub_cmd = POOL_SCRUB_RECENT,
8372+
};
8373+
ret = scrub_callback(zhp, &cbdata);
8374+
}
8375+
82978376
zpool_close(zhp);
82988377

82998378
nvlist_free(policy);
@@ -8395,66 +8474,6 @@ zpool_do_reopen(int argc, char **argv)
83958474
return (ret);
83968475
}
83978476

8398-
typedef struct scrub_cbdata {
8399-
int cb_type;
8400-
pool_scrub_cmd_t cb_scrub_cmd;
8401-
time_t cb_date_start;
8402-
time_t cb_date_end;
8403-
} scrub_cbdata_t;
8404-
8405-
static boolean_t
8406-
zpool_has_checkpoint(zpool_handle_t *zhp)
8407-
{
8408-
nvlist_t *config, *nvroot;
8409-
8410-
config = zpool_get_config(zhp, NULL);
8411-
8412-
if (config != NULL) {
8413-
pool_checkpoint_stat_t *pcs = NULL;
8414-
uint_t c;
8415-
8416-
nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
8417-
(void) nvlist_lookup_uint64_array(nvroot,
8418-
ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c);
8419-
8420-
if (pcs == NULL || pcs->pcs_state == CS_NONE)
8421-
return (B_FALSE);
8422-
8423-
assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS ||
8424-
pcs->pcs_state == CS_CHECKPOINT_DISCARDING);
8425-
return (B_TRUE);
8426-
}
8427-
8428-
return (B_FALSE);
8429-
}
8430-
8431-
static int
8432-
scrub_callback(zpool_handle_t *zhp, void *data)
8433-
{
8434-
scrub_cbdata_t *cb = data;
8435-
int err;
8436-
8437-
/*
8438-
* Ignore faulted pools.
8439-
*/
8440-
if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
8441-
(void) fprintf(stderr, gettext("cannot scan '%s': pool is "
8442-
"currently unavailable\n"), zpool_get_name(zhp));
8443-
return (1);
8444-
}
8445-
8446-
err = zpool_scan_range(zhp, cb->cb_type, cb->cb_scrub_cmd,
8447-
cb->cb_date_start, cb->cb_date_end);
8448-
if (err == 0 && zpool_has_checkpoint(zhp) &&
8449-
cb->cb_type == POOL_SCAN_SCRUB) {
8450-
(void) printf(gettext("warning: will not scrub state that "
8451-
"belongs to the checkpoint of pool '%s'\n"),
8452-
zpool_get_name(zhp));
8453-
}
8454-
8455-
return (err != 0);
8456-
}
8457-
84588477
static int
84598478
wait_callback(zpool_handle_t *zhp, void *data)
84608479
{
@@ -8490,14 +8509,15 @@ struct zpool_scrub_option {
84908509
};
84918510

84928511
/*
8493-
* zpool scrub [-e | -s | -p | -C | -E | -S] [-w] [-a | <pool> ...]
8512+
* zpool scrub [-e | -s | -p | -C | -E | -S | -R] [-w] [-a | <pool> ...]
84948513
*
84958514
* -a Scrub all pools.
84968515
* -e Only scrub blocks in the error log.
84978516
* -E End date of scrub.
84988517
* -S Start date of scrub.
84998518
* -s Stop. Stops any in-progress scrub.
85008519
* -p Pause. Pause in-progress scrub.
8520+
* -R Scrub only recent data.
85018521
* -w Wait. Blocks until scrub has completed.
85028522
* -C Scrub from last saved txg.
85038523
*/
@@ -8516,11 +8536,12 @@ zpool_do_scrub(int argc, char **argv)
85168536
struct zpool_scrub_option is_error_scrub = {'e', B_FALSE};
85178537
struct zpool_scrub_option is_pause = {'p', B_FALSE};
85188538
struct zpool_scrub_option is_stop = {'s', B_FALSE};
8539+
struct zpool_scrub_option is_recent = {'R', B_FALSE};
85198540
struct zpool_scrub_option is_txg_continue = {'C', B_FALSE};
85208541
struct zpool_scrub_option scrub_all = {'a', B_FALSE};
85218542

85228543
/* check options */
8523-
while ((c = getopt(argc, argv, "aspweCE:S:")) != -1) {
8544+
while ((c = getopt(argc, argv, "aspweCE:S:R")) != -1) {
85248545
switch (c) {
85258546
case 'a':
85268547
scrub_all.enabled = B_TRUE;
@@ -8544,6 +8565,9 @@ zpool_do_scrub(int argc, char **argv)
85448565
case 'p':
85458566
is_pause.enabled = B_TRUE;
85468567
break;
8568+
case 'R':
8569+
is_recent.enabled = B_TRUE;
8570+
break;
85478571
case 'w':
85488572
wait.enabled = B_TRUE;
85498573
break;
@@ -8564,9 +8588,13 @@ zpool_do_scrub(int argc, char **argv)
85648588
{&is_stop, &is_pause},
85658589
{&is_stop, &is_txg_continue},
85668590
{&is_stop, &is_error_scrub},
8591+
{&is_stop, &is_recent},
85678592
{&is_pause, &is_txg_continue},
85688593
{&is_pause, &is_error_scrub},
8594+
{&is_pause, &is_recent},
85698595
{&is_error_scrub, &is_txg_continue},
8596+
{&is_error_scrub, &is_recent},
8597+
{&is_recent, &is_txg_continue},
85708598
};
85718599

85728600
for (int i = 0; i < sizeof (scrub_exclusive_options) /
@@ -8591,6 +8619,8 @@ zpool_do_scrub(int argc, char **argv)
85918619
cb.cb_type = POOL_SCAN_NONE;
85928620
} else if (is_txg_continue.enabled) {
85938621
cb.cb_scrub_cmd = POOL_SCRUB_FROM_LAST_TXG;
8622+
} else if (is_recent.enabled) {
8623+
cb.cb_scrub_cmd = POOL_SCRUB_RECENT;
85948624
} else {
85958625
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
85968626
}

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
@@ -1100,6 +1100,7 @@ typedef enum pool_scrub_cmd {
11001100
POOL_SCRUB_NORMAL = 0,
11011101
POOL_SCRUB_PAUSE,
11021102
POOL_SCRUB_FROM_LAST_TXG,
1103+
POOL_SCRUB_RECENT,
11031104
POOL_SCRUB_FLAGS_END
11041105
} pool_scrub_cmd_t;
11051106

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

lib/libzfs/libzfs.abi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6095,7 +6095,8 @@
60956095
<enumerator name='POOL_SCRUB_NORMAL' value='0'/>
60966096
<enumerator name='POOL_SCRUB_PAUSE' value='1'/>
60976097
<enumerator name='POOL_SCRUB_FROM_LAST_TXG' value='2'/>
6098-
<enumerator name='POOL_SCRUB_FLAGS_END' value='3'/>
6098+
<enumerator name='POOL_SCRUB_RECENT' value='3'/>
6099+
<enumerator name='POOL_SCRUB_FLAGS_END' value='4'/>
60996100
</enum-decl>
61006101
<typedef-decl name='pool_scrub_cmd_t' type-id='a1474cbd' id='b51cf3c2'/>
61016102
<enum-decl name='zpool_errata' id='d9abbf54'>

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)