Skip to content

Commit

Permalink
Add last_scrubbed_txg property and option to scrub from last saved txg
Browse files Browse the repository at this point in the history
The `last_scrubbed_txg` property indicates the transaction
group (TXG) up to which the most recent scrub operation has
checked and repaired the dataset. This provides administrators
with insight into the data integrity status of their pool at a
specific point in time.

Sponsored-By: Wasabi Technology, Inc.
Sponsored-By: Klara Inc.
Signed-off-by: Mariusz Zaborski <[email protected]>
  • Loading branch information
oshogbo committed Jun 20, 2024
1 parent 815ac54 commit 68edb6e
Show file tree
Hide file tree
Showing 15 changed files with 184 additions and 7 deletions.
26 changes: 23 additions & 3 deletions cmd/zpool/zpool_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,8 @@ get_usage(zpool_help_t idx)
return (gettext("\tinitialize [-c | -s | -u] [-w] <pool> "
"[<device> ...]\n"));
case HELP_SCRUB:
return (gettext("\tscrub [-s | -p] [-w] [-e] <pool> ...\n"));
return (gettext("\tscrub [-s | -p] [-w] [-e] [-C] "
"<pool> ...\n"));
case HELP_RESILVER:
return (gettext("\tresilver <pool> ...\n"));
case HELP_TRIM:
Expand Down Expand Up @@ -7741,8 +7742,9 @@ wait_callback(zpool_handle_t *zhp, void *data)
}

/*
* zpool scrub [-s | -p] [-w] [-e] <pool> ...
* zpool scrub [-s | -p] [-w] [-e] [-C] <pool> ...
*
* -C Scrub from last saved txg.
* -e Only scrub blocks in the error log.
* -s Stop. Stops any in-progress scrub.
* -p Pause. Pause in-progress scrub.
Expand All @@ -7762,10 +7764,14 @@ zpool_do_scrub(int argc, char **argv)
boolean_t is_error_scrub = B_FALSE;
boolean_t is_pause = B_FALSE;
boolean_t is_stop = B_FALSE;
boolean_t is_txg_continue = B_FALSE;

/* check options */
while ((c = getopt(argc, argv, "spwe")) != -1) {
while ((c = getopt(argc, argv, "spweC")) != -1) {
switch (c) {
case 'C':
is_txg_continue = B_TRUE;
break;
case 'e':
is_error_scrub = B_TRUE;
break;
Expand All @@ -7789,6 +7795,18 @@ zpool_do_scrub(int argc, char **argv)
(void) fprintf(stderr, gettext("invalid option "
"combination :-s and -p are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_pause && is_txg_continue) {
(void) fprintf(stderr, gettext("invalid option "
"combination :-p and -C are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_stop && is_txg_continue) {
(void) fprintf(stderr, gettext("invalid option "
"combination :-s and -C are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_error_scrub && is_txg_continue) {
(void) fprintf(stderr, gettext("invalid option "
"combination :-e and -C are mutually exclusive\n"));
usage(B_FALSE);
} else {
if (is_error_scrub)
cb.cb_type = POOL_SCAN_ERRORSCRUB;
Expand All @@ -7797,6 +7815,8 @@ zpool_do_scrub(int argc, char **argv)
cb.cb_scrub_cmd = POOL_SCRUB_PAUSE;
} else if (is_stop) {
cb.cb_type = POOL_SCAN_NONE;
} else if (is_txg_continue) {
cb.cb_scrub_cmd = POOL_SCRUB_FROM_LAST_TXG;
} else {
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
}
Expand Down
1 change: 1 addition & 0 deletions include/sys/dmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ typedef struct dmu_buf {
#define DMU_POOL_CREATION_VERSION "creation_version"
#define DMU_POOL_SCAN "scan"
#define DMU_POOL_ERRORSCRUB "error_scrub"
#define DMU_POOL_LAST_SCRUBBED_TXG "last_scrubbed_txg"
#define DMU_POOL_FREE_BPOBJ "free_bpobj"
#define DMU_POOL_BPTREE_OBJ "bptree_obj"
#define DMU_POOL_EMPTY_BPOBJ "empty_bpobj"
Expand Down
2 changes: 2 additions & 0 deletions include/sys/fs/zfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ typedef enum {
ZPOOL_PROP_BCLONEUSED,
ZPOOL_PROP_BCLONESAVED,
ZPOOL_PROP_BCLONERATIO,
ZPOOL_PROP_LAST_SCRUBBED_TXG,
ZPOOL_NUM_PROPS
} zpool_prop_t;

Expand Down Expand Up @@ -1069,6 +1070,7 @@ typedef enum pool_scan_func {
typedef enum pool_scrub_cmd {
POOL_SCRUB_NORMAL = 0,
POOL_SCRUB_PAUSE,
POOL_SCRUB_FROM_LAST_TXG,
POOL_SCRUB_FLAGS_END
} pool_scrub_cmd_t;

Expand Down
1 change: 1 addition & 0 deletions include/sys/spa.h
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,7 @@ extern uint64_t spa_get_deadman_failmode(spa_t *spa);
extern void spa_set_deadman_failmode(spa_t *spa, const char *failmode);
extern boolean_t spa_suspended(spa_t *spa);
extern uint64_t spa_bootfs(spa_t *spa);
extern uint64_t spa_get_last_scrubbed_txg(spa_t *spa);
extern uint64_t spa_delegation(spa_t *spa);
extern objset_t *spa_meta_objset(spa_t *spa);
extern space_map_t *spa_syncing_log_sm(spa_t *spa);
Expand Down
1 change: 1 addition & 0 deletions include/sys/spa_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ struct spa {
uint64_t spa_scan_pass_scrub_spent_paused; /* total paused */
uint64_t spa_scan_pass_exam; /* examined bytes per pass */
uint64_t spa_scan_pass_issued; /* issued bytes per pass */
uint64_t spa_scrubbed_last_txg; /* last txg scrubbed */

/* error scrub pause time in milliseconds */
uint64_t spa_scan_pass_errorscrub_pause;
Expand Down
1 change: 1 addition & 0 deletions lib/libzfs/libzfs_pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
case ZPOOL_PROP_MAXDNODESIZE:
case ZPOOL_PROP_BCLONESAVED:
case ZPOOL_PROP_BCLONEUSED:
case ZPOOL_PROP_LAST_SCRUBBED_TXG:
if (literal)
(void) snprintf(buf, len, "%llu",
(u_longlong_t)intval);
Expand Down
10 changes: 9 additions & 1 deletion man/man7/zpoolprops.7
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
.\" Copyright (c) 2021, Colm Buckley <[email protected]>
.\" Copyright (c) 2023, Klara Inc.
.\"
.Dd January 2, 2024
.Dd June 20, 2024
.Dt ZPOOLPROPS 7
.Os
.
Expand Down Expand Up @@ -129,6 +129,14 @@ A unique identifier for the pool.
The current health of the pool.
Health can be one of
.Sy ONLINE , DEGRADED , FAULTED , OFFLINE, REMOVED , UNAVAIL .
.It Sy last_scrubbed_txg
Indicates the transaction group (TXG) up to which the most recent scrub
operation has checked and repaired the dataset.
This provides insight into the data integrity status of their pool at
a specific point in time.
The
.Xr zpool-scrub 8
might be used to utilize this property.
.It Sy leaked
Space not released while
.Sy freeing
Expand Down
5 changes: 5 additions & 0 deletions man/man8/zpool-scrub.8
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
.Op Fl s Ns | Ns Fl p
.Op Fl w
.Op Fl e
.Op Fl C
.Ar pool Ns
.
.Sh DESCRIPTION
Expand Down Expand Up @@ -114,6 +115,10 @@ The pool must have been scrubbed at least once with the
feature enabled to use this option.
Error scrubbing cannot be run simultaneously with regular scrubbing or
resilvering, nor can it be run when a regular scrub is paused.
.It Fl C
Continue scrub from last saved txg (see zpool
.Sy last_scrubbed_txg
property).
.El
.Sh EXAMPLES
.Ss Example 1
Expand Down
3 changes: 3 additions & 0 deletions module/zcommon/zpool_prop.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ zpool_prop_init(void)
zprop_register_number(ZPOOL_PROP_BCLONERATIO, "bcloneratio", 0,
PROP_READONLY, ZFS_TYPE_POOL, "<1.00x or higher if cloned>",
"BCLONE_RATIO", B_FALSE, sfeatures);
zprop_register_number(ZPOOL_PROP_LAST_SCRUBBED_TXG,
"last_scrubbed_txg", 0, PROP_READONLY, ZFS_TYPE_POOL, "<txg>",
"LAST_SCRUBBED_TXG", B_FALSE, sfeatures);

/* default number properties */
zprop_register_number(ZPOOL_PROP_VERSION, "version", SPA_VERSION,
Expand Down
18 changes: 15 additions & 3 deletions module/zfs/dsl_scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ static int zfs_resilver_disable_defer = B_FALSE;
((scn)->scn_phys.scn_func == POOL_SCAN_SCRUB || \
(scn)->scn_phys.scn_func == POOL_SCAN_RESILVER)

#define DSL_SCAN_IS_SCRUB(scn) \
((scn)->scn_phys.scn_func == POOL_SCAN_SCRUB)

/*
* Enable/disable the processing of the free_bpobj object.
*/
Expand Down Expand Up @@ -1129,15 +1132,24 @@ dsl_scan_done(dsl_scan_t *scn, boolean_t complete, dmu_tx_t *tx)

spa_notify_waiters(spa);

if (dsl_scan_restarting(scn, tx))
if (dsl_scan_restarting(scn, tx)) {
spa_history_log_internal(spa, "scan aborted, restarting", tx,
"errors=%llu", (u_longlong_t)spa_approx_errlog_size(spa));
else if (!complete)
} else if (!complete) {
spa_history_log_internal(spa, "scan cancelled", tx,
"errors=%llu", (u_longlong_t)spa_approx_errlog_size(spa));
else
} else {
spa_history_log_internal(spa, "scan done", tx,
"errors=%llu", (u_longlong_t)spa_approx_errlog_size(spa));
if (DSL_SCAN_IS_SCRUB(scn)) {
VERIFY0(zap_update(dp->dp_meta_objset,
DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_LAST_SCRUBBED_TXG,
sizeof (uint64_t), 1,
&scn->scn_phys.scn_max_txg, tx));
spa->spa_scrubbed_last_txg = scn->scn_phys.scn_max_txg;
}
}

if (DSL_SCAN_IS_SCRUB_RESILVER(scn)) {
spa->spa_scrub_active = B_FALSE;
Expand Down
9 changes: 9 additions & 0 deletions module/zfs/spa.c
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,9 @@ spa_prop_get_config(spa_t *spa, nvlist_t **nvp)
spa_prop_add_list(*nvp, ZPOOL_PROP_BCLONERATIO, NULL,
brt_get_ratio(spa), src);

spa_prop_add_list(*nvp, ZPOOL_PROP_LAST_SCRUBBED_TXG, NULL,
spa_get_last_scrubbed_txg(spa), src);

spa_prop_add_list(*nvp, ZPOOL_PROP_HEALTH, NULL,
rvd->vdev_state, src);

Expand Down Expand Up @@ -4660,6 +4663,12 @@ spa_ld_get_props(spa_t *spa)
if (error != 0 && error != ENOENT)
return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO));

/* Load the last scrubbed txg. */
error = spa_dir_prop(spa, DMU_POOL_LAST_SCRUBBED_TXG,
&spa->spa_scrubbed_last_txg, B_FALSE);
if (error != 0 && error != ENOENT)
return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO));

/*
* Load the livelist deletion field. If a livelist is queued for
* deletion, indicate that in the spa
Expand Down
6 changes: 6 additions & 0 deletions module/zfs/spa_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2660,6 +2660,12 @@ spa_mode(spa_t *spa)
return (spa->spa_mode);
}

uint64_t
spa_get_last_scrubbed_txg(spa_t *spa)
{
return (spa->spa_scrubbed_last_txg);
}

uint64_t
spa_bootfs(spa_t *spa)
{
Expand Down
3 changes: 3 additions & 0 deletions module/zfs/zfs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,9 @@ zfs_ioc_pool_scrub(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
error = spa_scrub_pause_resume(spa, POOL_SCRUB_PAUSE);
} else if (scan_type == POOL_SCAN_NONE) {
error = spa_scan_stop(spa);
} else if (scan_cmd == POOL_SCRUB_FROM_LAST_TXG) {
error = spa_scan_range(spa, scan_type,
spa_get_last_scrubbed_txg(spa), 0);
} else {
error = spa_scan(spa, scan_type);
}
Expand Down
1 change: 1 addition & 0 deletions tests/zfs-tests/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/cli_root/zpool_scrub/zpool_scrub_multiple_copies.ksh \
functional/cli_root/zpool_scrub/zpool_scrub_offline_device.ksh \
functional/cli_root/zpool_scrub/zpool_scrub_print_repairing.ksh \
functional/cli_root/zpool_scrub/zpool_scrub_txg_continue_from_last.ksh \
functional/cli_root/zpool_scrub/zpool_error_scrub_001_pos.ksh \
functional/cli_root/zpool_scrub/zpool_error_scrub_002_pos.ksh \
functional/cli_root/zpool_scrub/zpool_error_scrub_003_pos.ksh \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or https://opensource.org/licenses/CDDL-1.0.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

# Copyright (c) 2023, Klara Inc.
#
# This software was developed by
# Mariusz Zaborski <[email protected]>
# under sponsorship from Wasabi Technology, Inc. and Klara Inc.

. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zpool_scrub/zpool_scrub.cfg
. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.kshlib

#
# DESCRIPTION:
# Verify scrub -C
#
# STRATEGY:
# 1. Create a pool and create one file.
# 2. Verify that the last_txg_scrub is 0.
# 3. Run scrub.
# 4. Verify that the last_txg_scrub is set.
# 5. Create second file.
# 6. Invalidate both files.
# 7. Run scrub only from last point.
# 8. Verify that only one file, that was created with newer txg,
# was detected.
#

verify_runnable "global"

function cleanup
{
log_must zinject -c all
log_must rm -f $mntpnt/f1
log_must rm -f $mntpnt/f2
}

log_onexit cleanup

log_assert "Verify scrub -C."

# Create one file.
mntpnt=$(get_prop mountpoint $TESTPOOL/$TESTFS)

log_must file_write -b 1048576 -c 10 -o create -d 0 -f $mntpnt/f1
log_must sync_pool $TESTPOOL true
f1txg=$(get_last_txg_synced $TESTPOOL)

# Verify that last_scrubbed_txg isn't set.
zpoollasttxg=$(zpool get -H -o value last_scrubbed_txg $TESTPOOL)
log_must [ $zpoollasttxg -eq 0 ]

# Run scrub.
log_must zpool scrub -w $TESTPOOL

# Verify that last_scrubbed_txg is set.
zpoollasttxg=$(zpool get -H -o value last_scrubbed_txg $TESTPOOL)
log_must [ $zpoollasttxg -ne 0 ]

# Create second file.
log_must file_write -b 1048576 -c 10 -o create -d 0 -f $mntpnt/f2
log_must sync_pool $TESTPOOL true
f2txg=$(get_last_txg_synced $TESTPOOL)

# Make sure that the sync txg are different.
log_must [ $f1txg -ne $f2txg ]

# Insert faults.
log_must zinject -a -t data -e io -T read $mntpnt/f1
log_must zinject -a -t data -e io -T read $mntpnt/f2

# Run scrub from last saved point.
log_must zpool scrub -w -C $TESTPOOL

# Verify that only newer file was detected.
log_mustnot eval "zpool status -v $TESTPOOL | grep '$mntpnt/f1'"
log_must eval "zpool status -v $TESTPOOL | grep '$mntpnt/f2'"

# Verify that both files are corrupted.
log_must zpool scrub -w $TESTPOOL
log_must eval "zpool status -v $TESTPOOL | grep '$mntpnt/f1'"
log_must eval "zpool status -v $TESTPOOL | grep '$mntpnt/f2'"

log_pass "Verified scrub -C show expected status."

0 comments on commit 68edb6e

Please sign in to comment.