Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 152 additions & 110 deletions cmd/zpool/zpool_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,8 @@ get_usage(zpool_help_t idx)
return (gettext("\tattach [-fsw] [-o property=value] "
"<pool> <device> <new-device>\n"));
case HELP_CLEAR:
return (gettext("\tclear [[--power]|[-nF]] <pool> [device]\n"));
return (gettext("\tclear [[--power]|[-nsF]] <pool> "
"[device]\n"));
case HELP_CREATE:
return (gettext("\tcreate [-fnd] [-o property=value] ... \n"
"\t [-O file-system-property=value] ... \n"
Expand Down Expand Up @@ -513,8 +514,8 @@ get_usage(zpool_help_t idx)
return (gettext("\tinitialize [-c | -s | -u] [-w] <-a | <pool> "
"[<device> ...]>\n"));
case HELP_SCRUB:
return (gettext("\tscrub [-e | -s | -p | -C | -E | -S] [-w] "
"<-a | <pool> [<pool> ...]>\n"));
return (gettext("\tscrub [-e | -s | -p | -C | -E | -S | -R] "
"[-w] <-a | <pool> [<pool> ...]>\n"));
case HELP_RESILVER:
return (gettext("\tresilver <pool> ...\n"));
case HELP_TRIM:
Expand Down Expand Up @@ -8155,8 +8156,74 @@ zpool_do_offline(int argc, char **argv)
return (ret);
}

typedef struct scrub_cbdata {
int cb_type;
pool_scrub_cmd_t cb_scrub_cmd;
time_t cb_date_start;
time_t cb_date_end;
} scrub_cbdata_t;

static boolean_t
zpool_has_checkpoint(zpool_handle_t *zhp)
{
nvlist_t *config, *nvroot;

config = zpool_get_config(zhp, NULL);

if (config != NULL) {
pool_checkpoint_stat_t *pcs = NULL;
uint_t c;

nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
(void) nvlist_lookup_uint64_array(nvroot,
ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c);

if (pcs == NULL || pcs->pcs_state == CS_NONE)
return (B_FALSE);

assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS ||
pcs->pcs_state == CS_CHECKPOINT_DISCARDING);
return (B_TRUE);
}

return (B_FALSE);
}

static int
zpool_scrub(zpool_handle_t *zhp, scrub_cbdata_t *cb)
{
int err;

/*
* Ignore faulted pools.
*/
if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
(void) fprintf(stderr, gettext("cannot scan '%s': pool is "
"currently unavailable\n"), zpool_get_name(zhp));
return (1);
}

err = zpool_scan_range(zhp, cb->cb_type, cb->cb_scrub_cmd,
cb->cb_date_start, cb->cb_date_end);
if (err == 0 && zpool_has_checkpoint(zhp) &&
cb->cb_type == POOL_SCAN_SCRUB) {
(void) printf(gettext("warning: will not scrub state that "
"belongs to the checkpoint of pool '%s'\n"),
zpool_get_name(zhp));
}

return (err != 0);
}

static int
scrub_callback(zpool_handle_t *zhp, void *data)
{
scrub_cbdata_t *cb = data;
return (zpool_scrub(zhp, cb));
}

/*
* zpool clear [-nF]|[--power] <pool> [device]
* zpool clear [-nsF]|[--power] <pool> [device]
*
* Clear all errors associated with a pool or a particular device.
*/
Expand All @@ -8169,6 +8236,7 @@ zpool_do_clear(int argc, char **argv)
boolean_t do_rewind = B_FALSE;
boolean_t xtreme_rewind = B_FALSE;
boolean_t is_power_on = B_FALSE;
boolean_t scrub = B_FALSE;
uint32_t rewind_policy = ZPOOL_NO_REWIND;
nvlist_t *policy = NULL;
zpool_handle_t *zhp;
Expand All @@ -8180,7 +8248,7 @@ zpool_do_clear(int argc, char **argv)
};

/* check options */
while ((c = getopt_long(argc, argv, "FnX", long_options,
while ((c = getopt_long(argc, argv, "FnsX", long_options,
NULL)) != -1) {
switch (c) {
case 'F':
Expand All @@ -8189,6 +8257,9 @@ zpool_do_clear(int argc, char **argv)
case 'n':
dryrun = B_TRUE;
break;
case 's':
scrub = B_TRUE;
break;
case 'X':
xtreme_rewind = B_TRUE;
break;
Expand Down Expand Up @@ -8256,6 +8327,14 @@ zpool_do_clear(int argc, char **argv)
if (zpool_clear(zhp, device, policy) != 0)
ret = 1;

if (ret == 0 && !dryrun && scrub) {
scrub_cbdata_t cbdata = {
.cb_type = POOL_SCAN_SCRUB,
.cb_scrub_cmd = POOL_SCRUB_RECENT,
};
ret = scrub_callback(zhp, &cbdata);
}

zpool_close(zhp);

nvlist_free(policy);
Expand Down Expand Up @@ -8357,66 +8436,6 @@ zpool_do_reopen(int argc, char **argv)
return (ret);
}

typedef struct scrub_cbdata {
int cb_type;
pool_scrub_cmd_t cb_scrub_cmd;
time_t cb_date_start;
time_t cb_date_end;
} scrub_cbdata_t;

static boolean_t
zpool_has_checkpoint(zpool_handle_t *zhp)
{
nvlist_t *config, *nvroot;

config = zpool_get_config(zhp, NULL);

if (config != NULL) {
pool_checkpoint_stat_t *pcs = NULL;
uint_t c;

nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
(void) nvlist_lookup_uint64_array(nvroot,
ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c);

if (pcs == NULL || pcs->pcs_state == CS_NONE)
return (B_FALSE);

assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS ||
pcs->pcs_state == CS_CHECKPOINT_DISCARDING);
return (B_TRUE);
}

return (B_FALSE);
}

static int
scrub_callback(zpool_handle_t *zhp, void *data)
{
scrub_cbdata_t *cb = data;
int err;

/*
* Ignore faulted pools.
*/
if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
(void) fprintf(stderr, gettext("cannot scan '%s': pool is "
"currently unavailable\n"), zpool_get_name(zhp));
return (1);
}

err = zpool_scan_range(zhp, cb->cb_type, cb->cb_scrub_cmd,
cb->cb_date_start, cb->cb_date_end);
if (err == 0 && zpool_has_checkpoint(zhp) &&
cb->cb_type == POOL_SCAN_SCRUB) {
(void) printf(gettext("warning: will not scrub state that "
"belongs to the checkpoint of pool '%s'\n"),
zpool_get_name(zhp));
}

return (err != 0);
}

static int
wait_callback(zpool_handle_t *zhp, void *data)
{
Expand Down Expand Up @@ -8446,15 +8465,21 @@ date_string_to_sec(const char *timestr, boolean_t rounding)
return (mktime(&tm) + adjustment);
}

struct zpool_scrub_option {
char name;
boolean_t enabled;
};

/*
* zpool scrub [-e | -s | -p | -C | -E | -S] [-w] [-a | <pool> ...]
* zpool scrub [-e | -s | -p | -C | -E | -S | -R] [-w] [-a | <pool> ...]
*
* -a Scrub all pools.
* -e Only scrub blocks in the error log.
* -E End date of scrub.
* -S Start date of scrub.
* -s Stop. Stops any in-progress scrub.
* -p Pause. Pause in-progress scrub.
* -R Scrub only recent data.
* -w Wait. Blocks until scrub has completed.
* -C Scrub from last saved txg.
*/
Expand All @@ -8463,27 +8488,28 @@ zpool_do_scrub(int argc, char **argv)
{
int c;
scrub_cbdata_t cb;
boolean_t wait = B_FALSE;
int error;

cb.cb_type = POOL_SCAN_SCRUB;
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
cb.cb_date_start = cb.cb_date_end = 0;

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;
boolean_t scrub_all = B_FALSE;
struct zpool_scrub_option wait = {'w', B_FALSE};
struct zpool_scrub_option is_error_scrub = {'e', B_FALSE};
struct zpool_scrub_option is_pause = {'p', B_FALSE};
struct zpool_scrub_option is_stop = {'s', B_FALSE};
struct zpool_scrub_option is_recent = {'R', B_FALSE};
struct zpool_scrub_option is_txg_continue = {'C', B_FALSE};
struct zpool_scrub_option scrub_all = {'a', B_FALSE};

/* check options */
while ((c = getopt(argc, argv, "aspweCE:S:")) != -1) {
while ((c = getopt(argc, argv, "aspweCE:S:R")) != -1) {
switch (c) {
case 'a':
scrub_all = B_TRUE;
scrub_all.enabled = B_TRUE;
break;
case 'e':
is_error_scrub = B_TRUE;
is_error_scrub.enabled = B_TRUE;
break;
case 'E':
/*
Expand All @@ -8493,19 +8519,22 @@ zpool_do_scrub(int argc, char **argv)
cb.cb_date_end = date_string_to_sec(optarg, B_TRUE);
break;
case 's':
is_stop = B_TRUE;
is_stop.enabled = B_TRUE;
break;
case 'S':
cb.cb_date_start = date_string_to_sec(optarg, B_FALSE);
break;
case 'p':
is_pause = B_TRUE;
is_pause.enabled = B_TRUE;
break;
case 'R':
is_recent.enabled = B_TRUE;
break;
case 'w':
wait = B_TRUE;
wait.enabled = B_TRUE;
break;
case 'C':
is_txg_continue = B_TRUE;
is_txg_continue.enabled = B_TRUE;
break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
Expand All @@ -8514,37 +8543,50 @@ zpool_do_scrub(int argc, char **argv)
}
}

if (is_pause && is_stop) {
(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;

if (is_pause) {
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;
struct {
struct zpool_scrub_option *op1;
struct zpool_scrub_option *op2;
} scrub_exclusive_options[] = {
{&is_stop, &is_pause},
{&is_stop, &is_txg_continue},
{&is_stop, &is_error_scrub},
{&is_stop, &is_recent},
{&is_pause, &is_txg_continue},
{&is_pause, &is_error_scrub},
{&is_pause, &is_recent},
{&is_error_scrub, &is_txg_continue},
{&is_error_scrub, &is_recent},
{&is_recent, &is_txg_continue},
};

for (int i = 0; i < sizeof (scrub_exclusive_options) /
sizeof (scrub_exclusive_options[0]); i++) {
if (scrub_exclusive_options[i].op1->enabled &&
scrub_exclusive_options[i].op2->enabled) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -%c and -%c are mutually "
"exclusive\n"),
scrub_exclusive_options[i].op1->name,
scrub_exclusive_options[i].op2->name);
usage(B_FALSE);
}
}

if (is_error_scrub.enabled)
cb.cb_type = POOL_SCAN_ERRORSCRUB;

if (is_pause.enabled) {
cb.cb_scrub_cmd = POOL_SCRUB_PAUSE;
} else if (is_stop.enabled) {
cb.cb_type = POOL_SCAN_NONE;
} else if (is_txg_continue.enabled) {
cb.cb_scrub_cmd = POOL_SCRUB_FROM_LAST_TXG;
} else if (is_recent.enabled) {
cb.cb_scrub_cmd = POOL_SCRUB_RECENT;
} else {
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
}

if ((cb.cb_date_start != 0 || cb.cb_date_end != 0) &&
cb.cb_scrub_cmd != POOL_SCRUB_NORMAL) {
(void) fprintf(stderr, gettext("invalid option combination: "
Expand All @@ -8558,7 +8600,7 @@ zpool_do_scrub(int argc, char **argv)
usage(B_FALSE);
}

if (wait && (cb.cb_type == POOL_SCAN_NONE ||
if (wait.enabled && (cb.cb_type == POOL_SCAN_NONE ||
cb.cb_scrub_cmd == POOL_SCRUB_PAUSE)) {
(void) fprintf(stderr, gettext("invalid option combination: "
"-w cannot be used with -p or -s\n"));
Expand All @@ -8568,15 +8610,15 @@ zpool_do_scrub(int argc, char **argv)
argc -= optind;
argv += optind;

if (argc < 1 && !scrub_all) {
if (argc < 1 && !scrub_all.enabled) {
(void) fprintf(stderr, gettext("missing pool name argument\n"));
usage(B_FALSE);
}

error = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
B_FALSE, scrub_callback, &cb);

if (wait && !error) {
if (wait.enabled && !error) {
zpool_wait_activity_t act = ZPOOL_WAIT_SCRUB;
error = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
B_FALSE, wait_callback, &act);
Expand Down
Loading