Skip to content

Commit ff5bea1

Browse files
oshogbo0mp
authored andcommitted
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 (cherry picked from commit 4b4e346)
1 parent 669a26e commit ff5bea1

File tree

17 files changed

+240
-24
lines changed

17 files changed

+240
-24
lines changed

cmd/zpool/zpool_main.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ get_usage(zpool_help_t idx)
400400
return (gettext("\tinitialize [-c | -s] [-w] <pool> "
401401
"[<device> ...]\n"));
402402
case HELP_SCRUB:
403-
return (gettext("\tscrub [-s | -p] [-w] [-e] <pool> ...\n"));
403+
return (gettext("\tscrub [-e | -s | -p | -C] [-w] "
404+
"<pool> ...\n"));
404405
case HELP_RESILVER:
405406
return (gettext("\tresilver <pool> ...\n"));
406407
case HELP_TRIM:
@@ -7383,12 +7384,13 @@ wait_callback(zpool_handle_t *zhp, void *data)
73837384
}
73847385

73857386
/*
7386-
* zpool scrub [-s | -p] [-w] [-e] <pool> ...
7387+
* zpool scrub [-e | -s | -p | -C] [-w] <pool> ...
73877388
*
73887389
* -e Only scrub blocks in the error log.
73897390
* -s Stop. Stops any in-progress scrub.
73907391
* -p Pause. Pause in-progress scrub.
73917392
* -w Wait. Blocks until scrub has completed.
7393+
* -C Scrub from last saved txg.
73927394
*/
73937395
int
73947396
zpool_do_scrub(int argc, char **argv)
@@ -7404,9 +7406,10 @@ zpool_do_scrub(int argc, char **argv)
74047406
boolean_t is_error_scrub = B_FALSE;
74057407
boolean_t is_pause = B_FALSE;
74067408
boolean_t is_stop = B_FALSE;
7409+
boolean_t is_txg_continue = B_FALSE;
74077410

74087411
/* check options */
7409-
while ((c = getopt(argc, argv, "spwe")) != -1) {
7412+
while ((c = getopt(argc, argv, "spweC")) != -1) {
74107413
switch (c) {
74117414
case 'e':
74127415
is_error_scrub = B_TRUE;
@@ -7420,6 +7423,9 @@ zpool_do_scrub(int argc, char **argv)
74207423
case 'w':
74217424
wait = B_TRUE;
74227425
break;
7426+
case 'C':
7427+
is_txg_continue = B_TRUE;
7428+
break;
74237429
case '?':
74247430
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
74257431
optopt);
@@ -7431,6 +7437,18 @@ zpool_do_scrub(int argc, char **argv)
74317437
(void) fprintf(stderr, gettext("invalid option "
74327438
"combination :-s and -p are mutually exclusive\n"));
74337439
usage(B_FALSE);
7440+
} else if (is_pause && is_txg_continue) {
7441+
(void) fprintf(stderr, gettext("invalid option "
7442+
"combination :-p and -C are mutually exclusive\n"));
7443+
usage(B_FALSE);
7444+
} else if (is_stop && is_txg_continue) {
7445+
(void) fprintf(stderr, gettext("invalid option "
7446+
"combination :-s and -C are mutually exclusive\n"));
7447+
usage(B_FALSE);
7448+
} else if (is_error_scrub && is_txg_continue) {
7449+
(void) fprintf(stderr, gettext("invalid option "
7450+
"combination :-e and -C are mutually exclusive\n"));
7451+
usage(B_FALSE);
74347452
} else {
74357453
if (is_error_scrub)
74367454
cb.cb_type = POOL_SCAN_ERRORSCRUB;
@@ -7439,6 +7457,8 @@ zpool_do_scrub(int argc, char **argv)
74397457
cb.cb_scrub_cmd = POOL_SCRUB_PAUSE;
74407458
} else if (is_stop) {
74417459
cb.cb_type = POOL_SCAN_NONE;
7460+
} else if (is_txg_continue) {
7461+
cb.cb_scrub_cmd = POOL_SCRUB_FROM_LAST_TXG;
74427462
} else {
74437463
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
74447464
}

include/sys/dmu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ typedef struct dmu_buf {
375375
#define DMU_POOL_CREATION_VERSION "creation_version"
376376
#define DMU_POOL_SCAN "scan"
377377
#define DMU_POOL_ERRORSCRUB "error_scrub"
378+
#define DMU_POOL_LAST_SCRUBBED_TXG "last_scrubbed_txg"
378379
#define DMU_POOL_FREE_BPOBJ "free_bpobj"
379380
#define DMU_POOL_BPTREE_OBJ "bptree_obj"
380381
#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
@@ -178,6 +178,12 @@ typedef struct dsl_scan {
178178
dsl_errorscrub_phys_t errorscrub_phys;
179179
} dsl_scan_t;
180180

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

183189
void scan_init(void);
@@ -188,7 +194,8 @@ void dsl_scan_setup_sync(void *, dmu_tx_t *);
188194
void dsl_scan_fini(struct dsl_pool *dp);
189195
void dsl_scan_sync(struct dsl_pool *, dmu_tx_t *);
190196
int dsl_scan_cancel(struct dsl_pool *);
191-
int dsl_scan(struct dsl_pool *, pool_scan_func_t);
197+
int dsl_scan(struct dsl_pool *, pool_scan_func_t, uint64_t starttxg,
198+
uint64_t txgend);
192199
void dsl_scan_assess_vdev(struct dsl_pool *dp, vdev_t *vd);
193200
boolean_t dsl_scan_scrubbing(const struct dsl_pool *dp);
194201
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
@@ -247,6 +247,7 @@ typedef enum {
247247
ZPOOL_PROP_LOAD_GUID,
248248
ZPOOL_PROP_AUTOTRIM,
249249
ZPOOL_PROP_COMPATIBILITY,
250+
ZPOOL_PROP_LAST_SCRUBBED_TXG,
250251
ZPOOL_NUM_PROPS
251252
} zpool_prop_t;
252253

@@ -950,6 +951,7 @@ typedef enum pool_scan_func {
950951
typedef enum pool_scrub_cmd {
951952
POOL_SCRUB_NORMAL = 0,
952953
POOL_SCRUB_PAUSE,
954+
POOL_SCRUB_FROM_LAST_TXG,
953955
POOL_SCRUB_FLAGS_END
954956
} pool_scrub_cmd_t;
955957

include/sys/spa.h

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

845845
/* scanning */
846846
extern int spa_scan(spa_t *spa, pool_scan_func_t func);
847+
extern int spa_scan_range(spa_t *spa, pool_scan_func_t func, uint64_t txgstart,
848+
uint64_t txgend);
847849
extern int spa_scan_stop(spa_t *spa);
848850
extern int spa_scrub_pause_resume(spa_t *spa, pool_scrub_cmd_t flag);
849851

@@ -1097,6 +1099,7 @@ extern uint64_t spa_get_deadman_failmode(spa_t *spa);
10971099
extern void spa_set_deadman_failmode(spa_t *spa, const char *failmode);
10981100
extern boolean_t spa_suspended(spa_t *spa);
10991101
extern uint64_t spa_bootfs(spa_t *spa);
1102+
extern uint64_t spa_get_last_scrubbed_txg(spa_t *spa);
11001103
extern uint64_t spa_delegation(spa_t *spa);
11011104
extern objset_t *spa_meta_objset(spa_t *spa);
11021105
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
@@ -300,6 +300,7 @@ struct spa {
300300
uint64_t spa_scan_pass_scrub_spent_paused; /* total paused */
301301
uint64_t spa_scan_pass_exam; /* examined bytes per pass */
302302
uint64_t spa_scan_pass_issued; /* issued bytes per pass */
303+
uint64_t spa_scrubbed_last_txg; /* last txg scrubbed */
303304

304305
/* error scrub pause time in milliseconds */
305306
uint64_t spa_scan_pass_errorscrub_pause;

man/man7/zpoolprops.7

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
.\" Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
2828
.\" Copyright (c) 2021, Colm Buckley <[email protected]>
2929
.\"
30-
.Dd May 27, 2021
30+
.Dd November 18, 2024
3131
.Dt ZPOOLPROPS 7
3232
.Os
3333
.
@@ -101,6 +101,19 @@ Over time
101101
will decrease while
102102
.Sy free
103103
increases.
104+
.It Sy last_scrubbed_txg
105+
Indicates the transaction group (TXG) up to which the most recent scrub
106+
operation has checked and repaired the dataset.
107+
This provides insight into the data integrity status of their pool at
108+
a specific point in time.
109+
.Xr zpool-scrub 8
110+
can utilize this property to scan only data that has changed since the last
111+
scrub completed, when given the
112+
.Fl C
113+
flag.
114+
This property is not updated when performing an error scrub with the
115+
.Fl e
116+
flag.
104117
.It Sy leaked
105118
Space not released while
106119
.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 July 25, 2021
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
.Bl -tag -width "Exam"

module/zcommon/zpool_prop.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ zpool_prop_init(void)
104104
zprop_register_number(ZPOOL_PROP_DEDUPRATIO, "dedupratio", 0,
105105
PROP_READONLY, ZFS_TYPE_POOL, "<1.00x or higher if deduped>",
106106
"DEDUP");
107+
zprop_register_number(ZPOOL_PROP_LAST_SCRUBBED_TXG,
108+
"last_scrubbed_txg", 0, PROP_READONLY, ZFS_TYPE_POOL, "<txg>",
109+
"LAST_SCRUBBED_TXG");
107110

108111
/* default number properties */
109112
zprop_register_number(ZPOOL_PROP_VERSION, "version", SPA_VERSION,

module/zfs/dsl_scan.c

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ int zfs_resilver_disable_defer = 0; /* set to disable resilver deferring */
200200
((scn)->scn_phys.scn_func == POOL_SCAN_SCRUB || \
201201
(scn)->scn_phys.scn_func == POOL_SCAN_RESILVER)
202202

203+
#define DSL_SCAN_IS_SCRUB(scn) \
204+
((scn)->scn_phys.scn_func == POOL_SCAN_SCRUB)
205+
203206
/*
204207
* Enable/disable the processing of the free_bpobj object.
205208
*/
@@ -808,16 +811,16 @@ dsl_scan_setup_check(void *arg, dmu_tx_t *tx)
808811
void
809812
dsl_scan_setup_sync(void *arg, dmu_tx_t *tx)
810813
{
811-
(void) arg;
814+
setup_sync_arg_t *setup_sync_arg = (setup_sync_arg_t *)arg;
812815
dsl_scan_t *scn = dmu_tx_pool(tx)->dp_scan;
813-
pool_scan_func_t *funcp = arg;
814816
dmu_object_type_t ot = 0;
815817
dsl_pool_t *dp = scn->scn_dp;
816818
spa_t *spa = dp->dp_spa;
817819

818820

819821
ASSERT(!dsl_scan_is_running(scn));
820-
ASSERT(*funcp > POOL_SCAN_NONE && *funcp < POOL_SCAN_FUNCS);
822+
ASSERT3U(setup_sync_arg->func, >, POOL_SCAN_NONE);
823+
ASSERT3U(setup_sync_arg->func, <, POOL_SCAN_FUNCS);
821824
memset(&scn->scn_phys, 0, sizeof (scn->scn_phys));
822825

823826
/*
@@ -827,10 +830,14 @@ dsl_scan_setup_sync(void *arg, dmu_tx_t *tx)
827830
memset(&scn->errorscrub_phys, 0, sizeof (scn->errorscrub_phys));
828831
dsl_errorscrub_sync_state(scn, tx);
829832

830-
scn->scn_phys.scn_func = *funcp;
833+
scn->scn_phys.scn_func = setup_sync_arg->func;
831834
scn->scn_phys.scn_state = DSS_SCANNING;
832-
scn->scn_phys.scn_min_txg = 0;
833-
scn->scn_phys.scn_max_txg = tx->tx_txg;
835+
scn->scn_phys.scn_min_txg = setup_sync_arg->txgstart;
836+
if (setup_sync_arg->txgend == 0) {
837+
scn->scn_phys.scn_max_txg = tx->tx_txg;
838+
} else {
839+
scn->scn_phys.scn_max_txg = setup_sync_arg->txgend;
840+
}
834841
scn->scn_phys.scn_ddt_class_max = DDT_CLASSES - 1; /* the entire DDT */
835842
scn->scn_phys.scn_start_time = gethrestime_sec();
836843
scn->scn_phys.scn_errors = 0;
@@ -908,7 +915,7 @@ dsl_scan_setup_sync(void *arg, dmu_tx_t *tx)
908915

909916
spa_history_log_internal(spa, "scan setup", tx,
910917
"func=%u mintxg=%llu maxtxg=%llu",
911-
*funcp, (u_longlong_t)scn->scn_phys.scn_min_txg,
918+
setup_sync_arg->func, (u_longlong_t)scn->scn_phys.scn_min_txg,
912919
(u_longlong_t)scn->scn_phys.scn_max_txg);
913920
}
914921

@@ -918,10 +925,16 @@ dsl_scan_setup_sync(void *arg, dmu_tx_t *tx)
918925
* error scrub.
919926
*/
920927
int
921-
dsl_scan(dsl_pool_t *dp, pool_scan_func_t func)
928+
dsl_scan(dsl_pool_t *dp, pool_scan_func_t func, uint64_t txgstart,
929+
uint64_t txgend)
922930
{
923931
spa_t *spa = dp->dp_spa;
924932
dsl_scan_t *scn = dp->dp_scan;
933+
setup_sync_arg_t setup_sync_arg;
934+
935+
if (func != POOL_SCAN_SCRUB && (txgstart != 0 || txgend != 0)) {
936+
return (EINVAL);
937+
}
925938

926939
/*
927940
* Purge all vdev caches and probe all devices. We do this here
@@ -971,8 +984,13 @@ dsl_scan(dsl_pool_t *dp, pool_scan_func_t func)
971984
return (SET_ERROR(err));
972985
}
973986

987+
setup_sync_arg.func = func;
988+
setup_sync_arg.txgstart = txgstart;
989+
setup_sync_arg.txgend = txgend;
990+
974991
return (dsl_sync_task(spa_name(spa), dsl_scan_setup_check,
975-
dsl_scan_setup_sync, &func, 0, ZFS_SPACE_CHECK_EXTRA_RESERVED));
992+
dsl_scan_setup_sync, &setup_sync_arg, 0,
993+
ZFS_SPACE_CHECK_EXTRA_RESERVED));
976994
}
977995

978996
static void
@@ -1060,15 +1078,24 @@ dsl_scan_done(dsl_scan_t *scn, boolean_t complete, dmu_tx_t *tx)
10601078

10611079
spa_notify_waiters(spa);
10621080

1063-
if (dsl_scan_restarting(scn, tx))
1081+
if (dsl_scan_restarting(scn, tx)) {
10641082
spa_history_log_internal(spa, "scan aborted, restarting", tx,
10651083
"errors=%llu", (u_longlong_t)spa_approx_errlog_size(spa));
1066-
else if (!complete)
1084+
} else if (!complete) {
10671085
spa_history_log_internal(spa, "scan cancelled", tx,
10681086
"errors=%llu", (u_longlong_t)spa_approx_errlog_size(spa));
1069-
else
1087+
} else {
10701088
spa_history_log_internal(spa, "scan done", tx,
10711089
"errors=%llu", (u_longlong_t)spa_approx_errlog_size(spa));
1090+
if (DSL_SCAN_IS_SCRUB(scn)) {
1091+
VERIFY0(zap_update(dp->dp_meta_objset,
1092+
DMU_POOL_DIRECTORY_OBJECT,
1093+
DMU_POOL_LAST_SCRUBBED_TXG,
1094+
sizeof (uint64_t), 1,
1095+
&scn->scn_phys.scn_max_txg, tx));
1096+
spa->spa_scrubbed_last_txg = scn->scn_phys.scn_max_txg;
1097+
}
1098+
}
10721099

10731100
if (DSL_SCAN_IS_SCRUB_RESILVER(scn)) {
10741101
spa->spa_scrub_active = B_FALSE;

module/zfs/spa.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4615,6 +4615,12 @@ spa_ld_get_props(spa_t *spa)
46154615
if (error != 0 && error != ENOENT)
46164616
return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO));
46174617

4618+
/* Load the last scrubbed txg. */
4619+
error = spa_dir_prop(spa, DMU_POOL_LAST_SCRUBBED_TXG,
4620+
&spa->spa_scrubbed_last_txg, B_FALSE);
4621+
if (error != 0 && error != ENOENT)
4622+
return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO));
4623+
46184624
/*
46194625
* Load the livelist deletion field. If a livelist is queued for
46204626
* deletion, indicate that in the spa
@@ -8724,6 +8730,13 @@ spa_scan_stop(spa_t *spa)
87248730

87258731
int
87268732
spa_scan(spa_t *spa, pool_scan_func_t func)
8733+
{
8734+
return (spa_scan_range(spa, func, 0, 0));
8735+
}
8736+
8737+
int
8738+
spa_scan_range(spa_t *spa, pool_scan_func_t func, uint64_t txgstart,
8739+
uint64_t txgend)
87278740
{
87288741
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == 0);
87298742

@@ -8734,6 +8747,9 @@ spa_scan(spa_t *spa, pool_scan_func_t func)
87348747
!spa_feature_is_enabled(spa, SPA_FEATURE_RESILVER_DEFER))
87358748
return (SET_ERROR(ENOTSUP));
87368749

8750+
if (func != POOL_SCAN_SCRUB && (txgstart != 0 || txgend != 0))
8751+
return (SET_ERROR(ENOTSUP));
8752+
87378753
/*
87388754
* If a resilver was requested, but there is no DTL on a
87398755
* writeable leaf device, we have nothing to do.
@@ -8748,7 +8764,7 @@ spa_scan(spa_t *spa, pool_scan_func_t func)
87488764
!spa_feature_is_enabled(spa, SPA_FEATURE_HEAD_ERRLOG))
87498765
return (SET_ERROR(ENOTSUP));
87508766

8751-
return (dsl_scan(spa->spa_dsl_pool, func));
8767+
return (dsl_scan(spa->spa_dsl_pool, func, txgstart, txgend));
87528768
}
87538769

87548770
/*
@@ -10642,6 +10658,7 @@ EXPORT_SYMBOL(spa_l2cache_drop);
1064210658

1064310659
/* scanning */
1064410660
EXPORT_SYMBOL(spa_scan);
10661+
EXPORT_SYMBOL(spa_scan_range);
1064510662
EXPORT_SYMBOL(spa_scan_stop);
1064610663

1064710664
/* spa syncing */

0 commit comments

Comments
 (0)