Skip to content

Commit 4b4e346

Browse files
authored
Add ability to scrub from last scrubbed txg
Some users might want to scrub only new data because they would like to know if the new write wasn't corrupted. This PR adds possibility scrub only newly written data. This introduces new `last_scrubbed_txg` property, indicating the transaction group (TXG) up to which the most recent scrub operation has checked and repaired the dataset, so users can run scrub only from the last saved point. We use a scn_max_txg and scn_min_txg which are already built into scrub, to accomplish that. Reviewed-by: Allan Jude <[email protected]> Reviewed-by: Alexander Motin <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Mariusz Zaborski <[email protected]> Sponsored-By: Wasabi Technology, Inc. Sponsored-By: Klara Inc. Closes openzfs#16301
1 parent 1cd2419 commit 4b4e346

File tree

19 files changed

+264
-35
lines changed

19 files changed

+264
-35
lines changed

cmd/zpool/zpool_main.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,8 @@ get_usage(zpool_help_t idx)
512512
return (gettext("\tinitialize [-c | -s | -u] [-w] <pool> "
513513
"[<device> ...]\n"));
514514
case HELP_SCRUB:
515-
return (gettext("\tscrub [-s | -p] [-w] [-e] <pool> ...\n"));
515+
return (gettext("\tscrub [-e | -s | -p | -C] [-w] "
516+
"<pool> ...\n"));
516517
case HELP_RESILVER:
517518
return (gettext("\tresilver <pool> ...\n"));
518519
case HELP_TRIM:
@@ -8429,12 +8430,13 @@ wait_callback(zpool_handle_t *zhp, void *data)
84298430
}
84308431

84318432
/*
8432-
* zpool scrub [-s | -p] [-w] [-e] <pool> ...
8433+
* zpool scrub [-e | -s | -p | -C] [-w] <pool> ...
84338434
*
84348435
* -e Only scrub blocks in the error log.
84358436
* -s Stop. Stops any in-progress scrub.
84368437
* -p Pause. Pause in-progress scrub.
84378438
* -w Wait. Blocks until scrub has completed.
8439+
* -C Scrub from last saved txg.
84388440
*/
84398441
int
84408442
zpool_do_scrub(int argc, char **argv)
@@ -8450,9 +8452,10 @@ zpool_do_scrub(int argc, char **argv)
84508452
boolean_t is_error_scrub = B_FALSE;
84518453
boolean_t is_pause = B_FALSE;
84528454
boolean_t is_stop = B_FALSE;
8455+
boolean_t is_txg_continue = B_FALSE;
84538456

84548457
/* check options */
8455-
while ((c = getopt(argc, argv, "spwe")) != -1) {
8458+
while ((c = getopt(argc, argv, "spweC")) != -1) {
84568459
switch (c) {
84578460
case 'e':
84588461
is_error_scrub = B_TRUE;
@@ -8466,6 +8469,9 @@ zpool_do_scrub(int argc, char **argv)
84668469
case 'w':
84678470
wait = B_TRUE;
84688471
break;
8472+
case 'C':
8473+
is_txg_continue = B_TRUE;
8474+
break;
84698475
case '?':
84708476
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
84718477
optopt);
@@ -8477,6 +8483,18 @@ zpool_do_scrub(int argc, char **argv)
84778483
(void) fprintf(stderr, gettext("invalid option "
84788484
"combination :-s and -p are mutually exclusive\n"));
84798485
usage(B_FALSE);
8486+
} else if (is_pause && is_txg_continue) {
8487+
(void) fprintf(stderr, gettext("invalid option "
8488+
"combination :-p and -C are mutually exclusive\n"));
8489+
usage(B_FALSE);
8490+
} else if (is_stop && is_txg_continue) {
8491+
(void) fprintf(stderr, gettext("invalid option "
8492+
"combination :-s and -C are mutually exclusive\n"));
8493+
usage(B_FALSE);
8494+
} else if (is_error_scrub && is_txg_continue) {
8495+
(void) fprintf(stderr, gettext("invalid option "
8496+
"combination :-e and -C are mutually exclusive\n"));
8497+
usage(B_FALSE);
84808498
} else {
84818499
if (is_error_scrub)
84828500
cb.cb_type = POOL_SCAN_ERRORSCRUB;
@@ -8485,6 +8503,8 @@ zpool_do_scrub(int argc, char **argv)
84858503
cb.cb_scrub_cmd = POOL_SCRUB_PAUSE;
84868504
} else if (is_stop) {
84878505
cb.cb_type = POOL_SCAN_NONE;
8506+
} else if (is_txg_continue) {
8507+
cb.cb_scrub_cmd = POOL_SCRUB_FROM_LAST_TXG;
84888508
} else {
84898509
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
84908510
}

include/sys/dmu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ typedef struct dmu_buf {
381381
#define DMU_POOL_CREATION_VERSION "creation_version"
382382
#define DMU_POOL_SCAN "scan"
383383
#define DMU_POOL_ERRORSCRUB "error_scrub"
384+
#define DMU_POOL_LAST_SCRUBBED_TXG "last_scrubbed_txg"
384385
#define DMU_POOL_FREE_BPOBJ "free_bpobj"
385386
#define DMU_POOL_BPTREE_OBJ "bptree_obj"
386387
#define DMU_POOL_EMPTY_BPOBJ "empty_bpobj"

include/sys/dsl_scan.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@ typedef struct dsl_scan {
179179
dsl_errorscrub_phys_t errorscrub_phys;
180180
} dsl_scan_t;
181181

182+
typedef struct {
183+
pool_scan_func_t func;
184+
uint64_t txgstart;
185+
uint64_t txgend;
186+
} setup_sync_arg_t;
187+
182188
typedef struct dsl_scan_io_queue dsl_scan_io_queue_t;
183189

184190
void scan_init(void);
@@ -189,7 +195,8 @@ void dsl_scan_setup_sync(void *, dmu_tx_t *);
189195
void dsl_scan_fini(struct dsl_pool *dp);
190196
void dsl_scan_sync(struct dsl_pool *, dmu_tx_t *);
191197
int dsl_scan_cancel(struct dsl_pool *);
192-
int dsl_scan(struct dsl_pool *, pool_scan_func_t);
198+
int dsl_scan(struct dsl_pool *, pool_scan_func_t, uint64_t starttxg,
199+
uint64_t txgend);
193200
void dsl_scan_assess_vdev(struct dsl_pool *dp, vdev_t *vd);
194201
boolean_t dsl_scan_scrubbing(const struct dsl_pool *dp);
195202
boolean_t dsl_errorscrubbing(const struct dsl_pool *dp);

include/sys/fs/zfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ typedef enum {
265265
ZPOOL_PROP_DEDUP_TABLE_SIZE,
266266
ZPOOL_PROP_DEDUP_TABLE_QUOTA,
267267
ZPOOL_PROP_DEDUPCACHED,
268+
ZPOOL_PROP_LAST_SCRUBBED_TXG,
268269
ZPOOL_NUM_PROPS
269270
} zpool_prop_t;
270271

@@ -1088,6 +1089,7 @@ typedef enum pool_scan_func {
10881089
typedef enum pool_scrub_cmd {
10891090
POOL_SCRUB_NORMAL = 0,
10901091
POOL_SCRUB_PAUSE,
1092+
POOL_SCRUB_FROM_LAST_TXG,
10911093
POOL_SCRUB_FLAGS_END
10921094
} pool_scrub_cmd_t;
10931095

include/sys/spa.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,8 @@ extern void spa_l2cache_drop(spa_t *spa);
822822

823823
/* scanning */
824824
extern int spa_scan(spa_t *spa, pool_scan_func_t func);
825+
extern int spa_scan_range(spa_t *spa, pool_scan_func_t func, uint64_t txgstart,
826+
uint64_t txgend);
825827
extern int spa_scan_stop(spa_t *spa);
826828
extern int spa_scrub_pause_resume(spa_t *spa, pool_scrub_cmd_t flag);
827829

@@ -1080,6 +1082,7 @@ extern uint64_t spa_get_deadman_failmode(spa_t *spa);
10801082
extern void spa_set_deadman_failmode(spa_t *spa, const char *failmode);
10811083
extern boolean_t spa_suspended(spa_t *spa);
10821084
extern uint64_t spa_bootfs(spa_t *spa);
1085+
extern uint64_t spa_get_last_scrubbed_txg(spa_t *spa);
10831086
extern uint64_t spa_delegation(spa_t *spa);
10841087
extern objset_t *spa_meta_objset(spa_t *spa);
10851088
extern space_map_t *spa_syncing_log_sm(spa_t *spa);

include/sys/spa_impl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ struct spa {
318318
uint64_t spa_scan_pass_scrub_spent_paused; /* total paused */
319319
uint64_t spa_scan_pass_exam; /* examined bytes per pass */
320320
uint64_t spa_scan_pass_issued; /* issued bytes per pass */
321+
uint64_t spa_scrubbed_last_txg; /* last txg scrubbed */
321322

322323
/* error scrub pause time in milliseconds */
323324
uint64_t spa_scan_pass_errorscrub_pause;

lib/libzfs/libzfs.abi

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3132,7 +3132,8 @@
31323132
<enumerator name='ZPOOL_PROP_DEDUP_TABLE_SIZE' value='36'/>
31333133
<enumerator name='ZPOOL_PROP_DEDUP_TABLE_QUOTA' value='37'/>
31343134
<enumerator name='ZPOOL_PROP_DEDUPCACHED' value='38'/>
3135-
<enumerator name='ZPOOL_NUM_PROPS' value='39'/>
3135+
<enumerator name='ZPOOL_PROP_LAST_SCRUBBED_TXG' value='39'/>
3136+
<enumerator name='ZPOOL_NUM_PROPS' value='40'/>
31363137
</enum-decl>
31373138
<typedef-decl name='zpool_prop_t' type-id='af1ba157' id='5d0c23fb'/>
31383139
<typedef-decl name='regoff_t' type-id='95e97e5e' id='54a2a2a8'/>
@@ -5984,7 +5985,8 @@
59845985
<underlying-type type-id='9cac1fee'/>
59855986
<enumerator name='POOL_SCRUB_NORMAL' value='0'/>
59865987
<enumerator name='POOL_SCRUB_PAUSE' value='1'/>
5987-
<enumerator name='POOL_SCRUB_FLAGS_END' value='2'/>
5988+
<enumerator name='POOL_SCRUB_FROM_LAST_TXG' value='2'/>
5989+
<enumerator name='POOL_SCRUB_FLAGS_END' value='3'/>
59885990
</enum-decl>
59895991
<typedef-decl name='pool_scrub_cmd_t' type-id='a1474cbd' id='b51cf3c2'/>
59905992
<enum-decl name='zpool_errata' id='d9abbf54'>

man/man7/zpoolprops.7

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
.\" Copyright (c) 2021, Colm Buckley <[email protected]>
2929
.\" Copyright (c) 2023, Klara Inc.
3030
.\"
31-
.Dd July 29, 2024
31+
.Dd November 18, 2024
3232
.Dt ZPOOLPROPS 7
3333
.Os
3434
.
@@ -135,6 +135,19 @@ A unique identifier for the pool.
135135
The current health of the pool.
136136
Health can be one of
137137
.Sy ONLINE , DEGRADED , FAULTED , OFFLINE, REMOVED , UNAVAIL .
138+
.It Sy last_scrubbed_txg
139+
Indicates the transaction group (TXG) up to which the most recent scrub
140+
operation has checked and repaired the dataset.
141+
This provides insight into the data integrity status of their pool at
142+
a specific point in time.
143+
.Xr zpool-scrub 8
144+
can utilize this property to scan only data that has changed since the last
145+
scrub completed, when given the
146+
.Fl C
147+
flag.
148+
This property is not updated when performing an error scrub with the
149+
.Fl e
150+
flag.
138151
.It Sy leaked
139152
Space not released while
140153
.Sy freeing

man/man8/zpool-scrub.8

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
.\" Copyright 2017 Nexenta Systems, Inc.
2727
.\" Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
2828
.\"
29-
.Dd June 22, 2023
29+
.Dd November 18, 2024
3030
.Dt ZPOOL-SCRUB 8
3131
.Os
3232
.
@@ -36,9 +36,8 @@
3636
.Sh SYNOPSIS
3737
.Nm zpool
3838
.Cm scrub
39-
.Op Fl s Ns | Ns Fl p
39+
.Op Ns Fl e | Ns Fl p | Fl s Ns | Fl C Ns
4040
.Op Fl w
41-
.Op Fl e
4241
.Ar pool Ns
4342
.
4443
.Sh DESCRIPTION
@@ -114,6 +113,10 @@ The pool must have been scrubbed at least once with the
114113
feature enabled to use this option.
115114
Error scrubbing cannot be run simultaneously with regular scrubbing or
116115
resilvering, nor can it be run when a regular scrub is paused.
116+
.It Fl C
117+
Continue scrub from last saved txg (see zpool
118+
.Sy last_scrubbed_txg
119+
property).
117120
.El
118121
.Sh EXAMPLES
119122
.Ss Example 1

module/zcommon/zpool_prop.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ zpool_prop_init(void)
128128
zprop_register_number(ZPOOL_PROP_DEDUP_TABLE_SIZE, "dedup_table_size",
129129
0, PROP_READONLY, ZFS_TYPE_POOL, "<size>", "DDTSIZE", B_FALSE,
130130
sfeatures);
131+
zprop_register_number(ZPOOL_PROP_LAST_SCRUBBED_TXG,
132+
"last_scrubbed_txg", 0, PROP_READONLY, ZFS_TYPE_POOL, "<txg>",
133+
"LAST_SCRUBBED_TXG", B_FALSE, sfeatures);
131134

132135
/* default number properties */
133136
zprop_register_number(ZPOOL_PROP_VERSION, "version", SPA_VERSION,

module/zfs/dsl_scan.c

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ static uint_t zfs_resilver_defer_percent = 10;
231231
((scn)->scn_phys.scn_func == POOL_SCAN_SCRUB || \
232232
(scn)->scn_phys.scn_func == POOL_SCAN_RESILVER)
233233

234+
#define DSL_SCAN_IS_SCRUB(scn) \
235+
((scn)->scn_phys.scn_func == POOL_SCAN_SCRUB)
236+
234237
/*
235238
* Enable/disable the processing of the free_bpobj object.
236239
*/
@@ -855,15 +858,15 @@ dsl_scan_setup_check(void *arg, dmu_tx_t *tx)
855858
void
856859
dsl_scan_setup_sync(void *arg, dmu_tx_t *tx)
857860
{
858-
(void) arg;
861+
setup_sync_arg_t *setup_sync_arg = (setup_sync_arg_t *)arg;
859862
dsl_scan_t *scn = dmu_tx_pool(tx)->dp_scan;
860-
pool_scan_func_t *funcp = arg;
861863
dmu_object_type_t ot = 0;
862864
dsl_pool_t *dp = scn->scn_dp;
863865
spa_t *spa = dp->dp_spa;
864866

865867
ASSERT(!dsl_scan_is_running(scn));
866-
ASSERT(*funcp > POOL_SCAN_NONE && *funcp < POOL_SCAN_FUNCS);
868+
ASSERT3U(setup_sync_arg->func, >, POOL_SCAN_NONE);
869+
ASSERT3U(setup_sync_arg->func, <, POOL_SCAN_FUNCS);
867870
memset(&scn->scn_phys, 0, sizeof (scn->scn_phys));
868871

869872
/*
@@ -873,10 +876,14 @@ dsl_scan_setup_sync(void *arg, dmu_tx_t *tx)
873876
memset(&scn->errorscrub_phys, 0, sizeof (scn->errorscrub_phys));
874877
dsl_errorscrub_sync_state(scn, tx);
875878

876-
scn->scn_phys.scn_func = *funcp;
879+
scn->scn_phys.scn_func = setup_sync_arg->func;
877880
scn->scn_phys.scn_state = DSS_SCANNING;
878-
scn->scn_phys.scn_min_txg = 0;
879-
scn->scn_phys.scn_max_txg = tx->tx_txg;
881+
scn->scn_phys.scn_min_txg = setup_sync_arg->txgstart;
882+
if (setup_sync_arg->txgend == 0) {
883+
scn->scn_phys.scn_max_txg = tx->tx_txg;
884+
} else {
885+
scn->scn_phys.scn_max_txg = setup_sync_arg->txgend;
886+
}
880887
scn->scn_phys.scn_ddt_class_max = DDT_CLASSES - 1; /* the entire DDT */
881888
scn->scn_phys.scn_start_time = gethrestime_sec();
882889
scn->scn_phys.scn_errors = 0;
@@ -963,7 +970,7 @@ dsl_scan_setup_sync(void *arg, dmu_tx_t *tx)
963970

964971
spa_history_log_internal(spa, "scan setup", tx,
965972
"func=%u mintxg=%llu maxtxg=%llu",
966-
*funcp, (u_longlong_t)scn->scn_phys.scn_min_txg,
973+
setup_sync_arg->func, (u_longlong_t)scn->scn_phys.scn_min_txg,
967974
(u_longlong_t)scn->scn_phys.scn_max_txg);
968975
}
969976

@@ -973,10 +980,16 @@ dsl_scan_setup_sync(void *arg, dmu_tx_t *tx)
973980
* error scrub.
974981
*/
975982
int
976-
dsl_scan(dsl_pool_t *dp, pool_scan_func_t func)
983+
dsl_scan(dsl_pool_t *dp, pool_scan_func_t func, uint64_t txgstart,
984+
uint64_t txgend)
977985
{
978986
spa_t *spa = dp->dp_spa;
979987
dsl_scan_t *scn = dp->dp_scan;
988+
setup_sync_arg_t setup_sync_arg;
989+
990+
if (func != POOL_SCAN_SCRUB && (txgstart != 0 || txgend != 0)) {
991+
return (EINVAL);
992+
}
980993

981994
/*
982995
* Purge all vdev caches and probe all devices. We do this here
@@ -1027,8 +1040,13 @@ dsl_scan(dsl_pool_t *dp, pool_scan_func_t func)
10271040
return (SET_ERROR(err));
10281041
}
10291042

1043+
setup_sync_arg.func = func;
1044+
setup_sync_arg.txgstart = txgstart;
1045+
setup_sync_arg.txgend = txgend;
1046+
10301047
return (dsl_sync_task(spa_name(spa), dsl_scan_setup_check,
1031-
dsl_scan_setup_sync, &func, 0, ZFS_SPACE_CHECK_EXTRA_RESERVED));
1048+
dsl_scan_setup_sync, &setup_sync_arg, 0,
1049+
ZFS_SPACE_CHECK_EXTRA_RESERVED));
10321050
}
10331051

10341052
static void
@@ -1116,15 +1134,24 @@ dsl_scan_done(dsl_scan_t *scn, boolean_t complete, dmu_tx_t *tx)
11161134

11171135
spa_notify_waiters(spa);
11181136

1119-
if (dsl_scan_restarting(scn, tx))
1137+
if (dsl_scan_restarting(scn, tx)) {
11201138
spa_history_log_internal(spa, "scan aborted, restarting", tx,
11211139
"errors=%llu", (u_longlong_t)spa_approx_errlog_size(spa));
1122-
else if (!complete)
1140+
} else if (!complete) {
11231141
spa_history_log_internal(spa, "scan cancelled", tx,
11241142
"errors=%llu", (u_longlong_t)spa_approx_errlog_size(spa));
1125-
else
1143+
} else {
11261144
spa_history_log_internal(spa, "scan done", tx,
11271145
"errors=%llu", (u_longlong_t)spa_approx_errlog_size(spa));
1146+
if (DSL_SCAN_IS_SCRUB(scn)) {
1147+
VERIFY0(zap_update(dp->dp_meta_objset,
1148+
DMU_POOL_DIRECTORY_OBJECT,
1149+
DMU_POOL_LAST_SCRUBBED_TXG,
1150+
sizeof (uint64_t), 1,
1151+
&scn->scn_phys.scn_max_txg, tx));
1152+
spa->spa_scrubbed_last_txg = scn->scn_phys.scn_max_txg;
1153+
}
1154+
}
11281155

11291156
if (DSL_SCAN_IS_SCRUB_RESILVER(scn)) {
11301157
spa->spa_scrub_active = B_FALSE;
@@ -4330,14 +4357,18 @@ dsl_scan_sync(dsl_pool_t *dp, dmu_tx_t *tx)
43304357
* current scan progress is below zfs_resilver_defer_percent.
43314358
*/
43324359
if (dsl_scan_restarting(scn, tx) || restart_early) {
4333-
pool_scan_func_t func = POOL_SCAN_SCRUB;
4360+
setup_sync_arg_t setup_sync_arg = {
4361+
.func = POOL_SCAN_SCRUB,
4362+
.txgstart = 0,
4363+
.txgend = 0,
4364+
};
43344365
dsl_scan_done(scn, B_FALSE, tx);
43354366
if (vdev_resilver_needed(spa->spa_root_vdev, NULL, NULL))
4336-
func = POOL_SCAN_RESILVER;
4367+
setup_sync_arg.func = POOL_SCAN_RESILVER;
43374368
zfs_dbgmsg("restarting scan func=%u on %s txg=%llu early=%d",
4338-
func, dp->dp_spa->spa_name, (longlong_t)tx->tx_txg,
4339-
restart_early);
4340-
dsl_scan_setup_sync(&func, tx);
4369+
setup_sync_arg.func, dp->dp_spa->spa_name,
4370+
(longlong_t)tx->tx_txg, restart_early);
4371+
dsl_scan_setup_sync(&setup_sync_arg, tx);
43414372
}
43424373

43434374
/*

0 commit comments

Comments
 (0)