Skip to content

Commit 30edf88

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 0307d9c commit 30edf88

File tree

17 files changed

+556
-70
lines changed

17 files changed

+556
-70
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:
@@ -8144,8 +8145,74 @@ zpool_do_offline(int argc, char **argv)
81448145
return (ret);
81458146
}
81468147

8148+
typedef struct scrub_cbdata {
8149+
int cb_type;
8150+
pool_scrub_cmd_t cb_scrub_cmd;
8151+
time_t cb_date_start;
8152+
time_t cb_date_end;
8153+
} scrub_cbdata_t;
8154+
8155+
static boolean_t
8156+
zpool_has_checkpoint(zpool_handle_t *zhp)
8157+
{
8158+
nvlist_t *config, *nvroot;
8159+
8160+
config = zpool_get_config(zhp, NULL);
8161+
8162+
if (config != NULL) {
8163+
pool_checkpoint_stat_t *pcs = NULL;
8164+
uint_t c;
8165+
8166+
nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
8167+
(void) nvlist_lookup_uint64_array(nvroot,
8168+
ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c);
8169+
8170+
if (pcs == NULL || pcs->pcs_state == CS_NONE)
8171+
return (B_FALSE);
8172+
8173+
assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS ||
8174+
pcs->pcs_state == CS_CHECKPOINT_DISCARDING);
8175+
return (B_TRUE);
8176+
}
8177+
8178+
return (B_FALSE);
8179+
}
8180+
8181+
static int
8182+
zpool_scrub(zpool_handle_t *zhp, scrub_cbdata_t *cb)
8183+
{
8184+
int err;
8185+
8186+
/*
8187+
* Ignore faulted pools.
8188+
*/
8189+
if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
8190+
(void) fprintf(stderr, gettext("cannot scan '%s': pool is "
8191+
"currently unavailable\n"), zpool_get_name(zhp));
8192+
return (1);
8193+
}
8194+
8195+
err = zpool_scan_range(zhp, cb->cb_type, cb->cb_scrub_cmd,
8196+
cb->cb_date_start, cb->cb_date_end);
8197+
if (err == 0 && zpool_has_checkpoint(zhp) &&
8198+
cb->cb_type == POOL_SCAN_SCRUB) {
8199+
(void) printf(gettext("warning: will not scrub state that "
8200+
"belongs to the checkpoint of pool '%s'\n"),
8201+
zpool_get_name(zhp));
8202+
}
8203+
8204+
return (err != 0);
8205+
}
8206+
8207+
static int
8208+
scrub_callback(zpool_handle_t *zhp, void *data)
8209+
{
8210+
scrub_cbdata_t *cb = data;
8211+
return (zpool_scrub(zhp, cb));
8212+
}
8213+
81478214
/*
8148-
* zpool clear [-nF]|[--power] <pool> [device]
8215+
* zpool clear [-nsF]|[--power] <pool> [device]
81498216
*
81508217
* Clear all errors associated with a pool or a particular device.
81518218
*/
@@ -8158,6 +8225,7 @@ zpool_do_clear(int argc, char **argv)
81588225
boolean_t do_rewind = B_FALSE;
81598226
boolean_t xtreme_rewind = B_FALSE;
81608227
boolean_t is_power_on = B_FALSE;
8228+
boolean_t scrub = B_FALSE;
81618229
uint32_t rewind_policy = ZPOOL_NO_REWIND;
81628230
nvlist_t *policy = NULL;
81638231
zpool_handle_t *zhp;
@@ -8169,7 +8237,7 @@ zpool_do_clear(int argc, char **argv)
81698237
};
81708238

81718239
/* check options */
8172-
while ((c = getopt_long(argc, argv, "FnX", long_options,
8240+
while ((c = getopt_long(argc, argv, "FnsX", long_options,
81738241
NULL)) != -1) {
81748242
switch (c) {
81758243
case 'F':
@@ -8178,6 +8246,9 @@ zpool_do_clear(int argc, char **argv)
81788246
case 'n':
81798247
dryrun = B_TRUE;
81808248
break;
8249+
case 's':
8250+
scrub = B_TRUE;
8251+
break;
81818252
case 'X':
81828253
xtreme_rewind = B_TRUE;
81838254
break;
@@ -8245,6 +8316,14 @@ zpool_do_clear(int argc, char **argv)
82458316
if (zpool_clear(zhp, device, policy) != 0)
82468317
ret = 1;
82478318

8319+
if (ret == 0 && !dryrun && scrub) {
8320+
scrub_cbdata_t cbdata = {
8321+
.cb_type = POOL_SCAN_SCRUB,
8322+
.cb_scrub_cmd = POOL_SCRUB_RECENT,
8323+
};
8324+
ret = scrub_callback(zhp, &cbdata);
8325+
}
8326+
82488327
zpool_close(zhp);
82498328

82508329
nvlist_free(policy);
@@ -8346,66 +8425,6 @@ zpool_do_reopen(int argc, char **argv)
83468425
return (ret);
83478426
}
83488427

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

84438462
/*
8444-
* zpool scrub [-e | -s | -p | -C | -E | -S] [-w] [-a | <pool> ...]
8463+
* zpool scrub [-e | -s | -p | -C | -E | -S | -R] [-w] [-a | <pool> ...]
84458464
*
84468465
* -a Scrub all pools.
84478466
* -e Only scrub blocks in the error log.
84488467
* -E End date of scrub.
84498468
* -S Start date of scrub.
84508469
* -s Stop. Stops any in-progress scrub.
84518470
* -p Pause. Pause in-progress scrub.
8471+
* -R Scrub only recent data.
84528472
* -w Wait. Blocks until scrub has completed.
84538473
* -C Scrub from last saved txg.
84548474
*/
@@ -8467,11 +8487,12 @@ zpool_do_scrub(int argc, char **argv)
84678487
struct zpool_scrub_option is_error_scrub = {'e', B_FALSE};
84688488
struct zpool_scrub_option is_pause = {'p', B_FALSE};
84698489
struct zpool_scrub_option is_stop = {'s', B_FALSE};
8490+
struct zpool_scrub_option is_recent = {'R', B_FALSE};
84708491
struct zpool_scrub_option is_txg_continue = {'C', B_FALSE};
84718492
struct zpool_scrub_option scrub_all = {'a', B_FALSE};
84728493

84738494
/* check options */
8474-
while ((c = getopt(argc, argv, "aspweCE:S:")) != -1) {
8495+
while ((c = getopt(argc, argv, "aspweCE:S:R")) != -1) {
84758496
switch (c) {
84768497
case 'a':
84778498
scrub_all.enabled = B_TRUE;
@@ -8495,6 +8516,9 @@ zpool_do_scrub(int argc, char **argv)
84958516
case 'p':
84968517
is_pause.enabled = B_TRUE;
84978518
break;
8519+
case 'R':
8520+
is_recent.enabled = B_TRUE;
8521+
break;
84988522
case 'w':
84998523
wait.enabled = B_TRUE;
85008524
break;
@@ -8515,9 +8539,13 @@ zpool_do_scrub(int argc, char **argv)
85158539
{&is_stop, &is_pause},
85168540
{&is_stop, &is_txg_continue},
85178541
{&is_stop, &is_error_scrub},
8542+
{&is_stop, &is_recent},
85188543
{&is_pause, &is_txg_continue},
85198544
{&is_pause, &is_error_scrub},
8545+
{&is_pause, &is_recent},
85208546
{&is_error_scrub, &is_txg_continue},
8547+
{&is_error_scrub, &is_recent},
8548+
{&is_recent, &is_txg_continue},
85218549
};
85228550

85238551
for (int i = 0; i < sizeof (scrub_exclusive_options) /
@@ -8542,6 +8570,8 @@ zpool_do_scrub(int argc, char **argv)
85428570
cb.cb_type = POOL_SCAN_NONE;
85438571
} else if (is_txg_continue.enabled) {
85448572
cb.cb_scrub_cmd = POOL_SCRUB_FROM_LAST_TXG;
8573+
} else if (is_recent.enabled) {
8574+
cb.cb_scrub_cmd = POOL_SCRUB_RECENT;
85458575
} else {
85468576
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
85478577
}

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)