Skip to content

Commit 0ea05c6

Browse files
alek-pbehlendorf
authored andcommitted
Implemented zpool scrub pause/resume
Currently, there is no way to pause a scrub. Pausing may be useful when the pool is busy with other I/O to preserve bandwidth. This patch adds the ability to pause and resume scrubbing. This is achieved by maintaining a persistent on-disk scrub state. While the state is 'paused' we do not scrub any more blocks. We do however perform regular scan housekeeping such as freeing async destroyed and deadlist blocks while paused. Reviewed by: Matthew Ahrens <[email protected]> Reviewed by: Thomas Caputi <[email protected]> Reviewed-by: Serapheim Dimitropoulos <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Alek Pinchuk <[email protected]> Closes #6167
1 parent 94b2566 commit 0ea05c6

File tree

17 files changed

+362
-124
lines changed

17 files changed

+362
-124
lines changed

cmd/zpool/zpool_main.c

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ get_usage(zpool_help_t idx)
342342
case HELP_REOPEN:
343343
return (gettext("\treopen <pool>\n"));
344344
case HELP_SCRUB:
345-
return (gettext("\tscrub [-s] <pool> ...\n"));
345+
return (gettext("\tscrub [-s | -p] <pool> ...\n"));
346346
case HELP_STATUS:
347347
return (gettext("\tstatus [-c [script1,script2,...]] [-gLPvxD]"
348348
"[-T d|u] [pool] ... [interval [count]]\n"));
@@ -5759,6 +5759,7 @@ typedef struct scrub_cbdata {
57595759
int cb_type;
57605760
int cb_argc;
57615761
char **cb_argv;
5762+
pool_scrub_cmd_t cb_scrub_cmd;
57625763
} scrub_cbdata_t;
57635764

57645765
int
@@ -5776,15 +5777,16 @@ scrub_callback(zpool_handle_t *zhp, void *data)
57765777
return (1);
57775778
}
57785779

5779-
err = zpool_scan(zhp, cb->cb_type);
5780+
err = zpool_scan(zhp, cb->cb_type, cb->cb_scrub_cmd);
57805781

57815782
return (err != 0);
57825783
}
57835784

57845785
/*
5785-
* zpool scrub [-s] <pool> ...
5786+
* zpool scrub [-s | -p] <pool> ...
57865787
*
57875788
* -s Stop. Stops any in-progress scrub.
5789+
* -p Pause. Pause in-progress scrub.
57885790
*/
57895791
int
57905792
zpool_do_scrub(int argc, char **argv)
@@ -5793,20 +5795,31 @@ zpool_do_scrub(int argc, char **argv)
57935795
scrub_cbdata_t cb;
57945796

57955797
cb.cb_type = POOL_SCAN_SCRUB;
5798+
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
57965799

57975800
/* check options */
5798-
while ((c = getopt(argc, argv, "s")) != -1) {
5801+
while ((c = getopt(argc, argv, "sp")) != -1) {
57995802
switch (c) {
58005803
case 's':
58015804
cb.cb_type = POOL_SCAN_NONE;
58025805
break;
5806+
case 'p':
5807+
cb.cb_scrub_cmd = POOL_SCRUB_PAUSE;
5808+
break;
58035809
case '?':
58045810
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
58055811
optopt);
58065812
usage(B_FALSE);
58075813
}
58085814
}
58095815

5816+
if (cb.cb_type == POOL_SCAN_NONE &&
5817+
cb.cb_scrub_cmd == POOL_SCRUB_PAUSE) {
5818+
(void) fprintf(stderr, gettext("invalid option combination: "
5819+
"-s and -p are mutually exclusive\n"));
5820+
usage(B_FALSE);
5821+
}
5822+
58105823
cb.cb_argc = argc;
58115824
cb.cb_argv = argv;
58125825
argc -= optind;
@@ -5826,7 +5839,7 @@ zpool_do_scrub(int argc, char **argv)
58265839
void
58275840
print_scan_status(pool_scan_stat_t *ps)
58285841
{
5829-
time_t start, end;
5842+
time_t start, end, pause;
58305843
uint64_t elapsed, mins_left, hours_left;
58315844
uint64_t pass_exam, examined, total;
58325845
uint_t rate;
@@ -5844,6 +5857,7 @@ print_scan_status(pool_scan_stat_t *ps)
58445857

58455858
start = ps->pss_start_time;
58465859
end = ps->pss_end_time;
5860+
pause = ps->pss_pass_scrub_pause;
58475861
zfs_nicebytes(ps->pss_processed, processed_buf, sizeof (processed_buf));
58485862

58495863
assert(ps->pss_func == POOL_SCAN_SCRUB ||
@@ -5886,8 +5900,17 @@ print_scan_status(pool_scan_stat_t *ps)
58865900
* Scan is in progress.
58875901
*/
58885902
if (ps->pss_func == POOL_SCAN_SCRUB) {
5889-
(void) printf(gettext("scrub in progress since %s"),
5890-
ctime(&start));
5903+
if (pause == 0) {
5904+
(void) printf(gettext("scrub in progress since %s"),
5905+
ctime(&start));
5906+
} else {
5907+
char buf[32];
5908+
struct tm *p = localtime(&pause);
5909+
(void) strftime(buf, sizeof (buf), "%a %b %e %T %Y", p);
5910+
(void) printf(gettext("scrub paused since %s\n"), buf);
5911+
(void) printf(gettext("\tscrub started on %s"),
5912+
ctime(&start));
5913+
}
58915914
} else if (ps->pss_func == POOL_SCAN_RESILVER) {
58925915
(void) printf(gettext("resilver in progress since %s"),
58935916
ctime(&start));
@@ -5899,6 +5922,7 @@ print_scan_status(pool_scan_stat_t *ps)
58995922

59005923
/* elapsed time for this pass */
59015924
elapsed = time(NULL) - ps->pss_pass_start;
5925+
elapsed -= ps->pss_pass_scrub_spent_paused;
59025926
elapsed = elapsed ? elapsed : 1;
59035927
pass_exam = ps->pss_pass_exam ? ps->pss_pass_exam : 1;
59045928
rate = pass_exam / elapsed;
@@ -5908,19 +5932,25 @@ print_scan_status(pool_scan_stat_t *ps)
59085932

59095933
zfs_nicebytes(examined, examined_buf, sizeof (examined_buf));
59105934
zfs_nicebytes(total, total_buf, sizeof (total_buf));
5911-
zfs_nicebytes(rate, rate_buf, sizeof (rate_buf));
59125935

59135936
/*
59145937
* do not print estimated time if hours_left is more than 30 days
5938+
* or we have a paused scrub
59155939
*/
5916-
(void) printf(gettext("\t%s scanned out of %s at %s/s"),
5917-
examined_buf, total_buf, rate_buf);
5918-
if (hours_left < (30 * 24)) {
5919-
(void) printf(gettext(", %lluh%um to go\n"),
5920-
(u_longlong_t)hours_left, (uint_t)(mins_left % 60));
5940+
if (pause == 0) {
5941+
zfs_nicebytes(rate, rate_buf, sizeof (rate_buf));
5942+
(void) printf(gettext("\t%s scanned out of %s at %s/s"),
5943+
examined_buf, total_buf, rate_buf);
5944+
if (hours_left < (30 * 24)) {
5945+
(void) printf(gettext(", %lluh%um to go\n"),
5946+
(u_longlong_t)hours_left, (uint_t)(mins_left % 60));
5947+
} else {
5948+
(void) printf(gettext(
5949+
", (scan is slow, no estimated time)\n"));
5950+
}
59215951
} else {
5922-
(void) printf(gettext(
5923-
", (scan is slow, no estimated time)\n"));
5952+
(void) printf(gettext("\t%s scanned out of %s\n"),
5953+
examined_buf, total_buf);
59245954
}
59255955

59265956
if (ps->pss_func == POOL_SCAN_RESILVER) {

include/libzfs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ typedef enum zfs_error {
147147
EZFS_DIFF, /* general failure of zfs diff */
148148
EZFS_DIFFDATA, /* bad zfs diff data */
149149
EZFS_POOLREADONLY, /* pool is in read-only mode */
150+
EZFS_SCRUB_PAUSED, /* scrub currently paused */
150151
EZFS_UNKNOWN
151152
} zfs_error_t;
152153

@@ -260,7 +261,7 @@ typedef struct splitflags {
260261
/*
261262
* Functions to manipulate pool and vdev state
262263
*/
263-
extern int zpool_scan(zpool_handle_t *, pool_scan_func_t);
264+
extern int zpool_scan(zpool_handle_t *, pool_scan_func_t, pool_scrub_cmd_t);
264265
extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
265266
extern int zpool_reguid(zpool_handle_t *);
266267
extern int zpool_reopen(zpool_handle_t *);

include/sys/dsl_scan.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
/*
2222
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2323
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
24+
* Copyright (c) 2017 Datto Inc.
2425
*/
2526

2627
#ifndef _SYS_DSL_SCAN_H
@@ -70,6 +71,7 @@ typedef struct dsl_scan_phys {
7071

7172
typedef enum dsl_scan_flags {
7273
DSF_VISIT_DS_AGAIN = 1<<0,
74+
DSF_SCRUB_PAUSED = 1<<1,
7375
} dsl_scan_flags_t;
7476

7577
#define DSL_SCAN_FLAGS_MASK (DSF_VISIT_DS_AGAIN)
@@ -84,8 +86,8 @@ typedef enum dsl_scan_flags {
8486
*
8587
* The following members of this structure direct the behavior of the scan:
8688
*
87-
* scn_pausing - a scan that cannot be completed in a single txg or
88-
* has exceeded its allotted time will need to pause.
89+
* scn_suspending - a scan that cannot be completed in a single txg or
90+
* has exceeded its allotted time will need to suspend.
8991
* When this flag is set the scanner will stop traversing
9092
* the pool and write out the current state to disk.
9193
*
@@ -107,7 +109,7 @@ typedef enum dsl_scan_flags {
107109
typedef struct dsl_scan {
108110
struct dsl_pool *scn_dp;
109111

110-
boolean_t scn_pausing;
112+
boolean_t scn_suspending;
111113
uint64_t scn_restart_txg;
112114
uint64_t scn_done_txg;
113115
uint64_t scn_sync_start_time;
@@ -117,8 +119,6 @@ typedef struct dsl_scan {
117119
boolean_t scn_is_bptree;
118120
boolean_t scn_async_destroying;
119121
boolean_t scn_async_stalled;
120-
121-
/* for debugging / information */
122122
uint64_t scn_visited_this_txg;
123123

124124
dsl_scan_phys_t scn_phys;
@@ -129,6 +129,8 @@ void dsl_scan_fini(struct dsl_pool *dp);
129129
void dsl_scan_sync(struct dsl_pool *, dmu_tx_t *);
130130
int dsl_scan_cancel(struct dsl_pool *);
131131
int dsl_scan(struct dsl_pool *, pool_scan_func_t);
132+
boolean_t dsl_scan_scrubbing(const struct dsl_pool *dp);
133+
int dsl_scrub_set_pause_resume(const struct dsl_pool *dp, pool_scrub_cmd_t cmd);
132134
void dsl_resilver_restart(struct dsl_pool *, uint64_t txg);
133135
boolean_t dsl_scan_resilvering(struct dsl_pool *dp);
134136
boolean_t dsl_dataset_unstable(struct dsl_dataset *ds);
@@ -139,6 +141,7 @@ void dsl_scan_ds_snapshotted(struct dsl_dataset *ds, struct dmu_tx *tx);
139141
void dsl_scan_ds_clone_swapped(struct dsl_dataset *ds1, struct dsl_dataset *ds2,
140142
struct dmu_tx *tx);
141143
boolean_t dsl_scan_active(dsl_scan_t *scn);
144+
boolean_t dsl_scan_is_paused_scrub(const dsl_scan_t *scn);
142145

143146
#ifdef __cplusplus
144147
}

include/sys/fs/zfs.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,16 @@ typedef enum pool_scan_func {
765765
POOL_SCAN_FUNCS
766766
} pool_scan_func_t;
767767

768+
/*
769+
* Used to control scrub pause and resume.
770+
*/
771+
typedef enum pool_scrub_cmd {
772+
POOL_SCRUB_NORMAL = 0,
773+
POOL_SCRUB_PAUSE,
774+
POOL_SCRUB_FLAGS_END
775+
} pool_scrub_cmd_t;
776+
777+
768778
/*
769779
* ZIO types. Needed to interpret vdev statistics below.
770780
*/
@@ -797,6 +807,9 @@ typedef struct pool_scan_stat {
797807
/* values not stored on disk */
798808
uint64_t pss_pass_exam; /* examined bytes per scan pass */
799809
uint64_t pss_pass_start; /* start time of a scan pass */
810+
uint64_t pss_pass_scrub_pause; /* pause time of a scurb pass */
811+
/* cumulative time scrub spent paused, needed for rate calculation */
812+
uint64_t pss_pass_scrub_spent_paused;
800813
} pool_scan_stat_t;
801814

802815
typedef enum dsl_scan_state {

include/sys/spa.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
2525
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
2626
* Copyright 2013 Saso Kiselkov. All rights reserved.
27+
* Copyright (c) 2017 Datto Inc.
2728
*/
2829

2930
#ifndef _SYS_SPA_H
@@ -657,6 +658,7 @@ extern void spa_l2cache_drop(spa_t *spa);
657658
/* scanning */
658659
extern int spa_scan(spa_t *spa, pool_scan_func_t func);
659660
extern int spa_scan_stop(spa_t *spa);
661+
extern int spa_scrub_pause_resume(spa_t *spa, pool_scrub_cmd_t flag);
660662

661663
/* spa syncing */
662664
extern void spa_sync(spa_t *spa, uint64_t txg); /* only for DMU use */

include/sys/spa_impl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
2626
* Copyright 2013 Saso Kiselkov. All rights reserved.
2727
* Copyright (c) 2016 Actifio, Inc. All rights reserved.
28+
* Copyright (c) 2017 Datto Inc.
2829
*/
2930

3031
#ifndef _SYS_SPA_IMPL_H
@@ -193,6 +194,8 @@ struct spa {
193194
uint8_t spa_scrub_started; /* started since last boot */
194195
uint8_t spa_scrub_reopen; /* scrub doing vdev_reopen */
195196
uint64_t spa_scan_pass_start; /* start time per pass/reboot */
197+
uint64_t spa_scan_pass_scrub_pause; /* scrub pause time */
198+
uint64_t spa_scan_pass_scrub_spent_paused; /* total paused */
196199
uint64_t spa_scan_pass_exam; /* examined bytes per pass */
197200
kmutex_t spa_async_lock; /* protect async state */
198201
kthread_t *spa_async_thread; /* thread doing async task */

lib/libzfs/libzfs_pool.c

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,22 +1898,39 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
18981898
* Scan the pool.
18991899
*/
19001900
int
1901-
zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func)
1901+
zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func, pool_scrub_cmd_t cmd)
19021902
{
19031903
zfs_cmd_t zc = {"\0"};
19041904
char msg[1024];
1905+
int err;
19051906
libzfs_handle_t *hdl = zhp->zpool_hdl;
19061907

19071908
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
19081909
zc.zc_cookie = func;
1910+
zc.zc_flags = cmd;
1911+
1912+
if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0)
1913+
return (0);
1914+
1915+
err = errno;
19091916

1910-
if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0 ||
1911-
(errno == ENOENT && func != POOL_SCAN_NONE))
1917+
/* ECANCELED on a scrub means we resumed a paused scrub */
1918+
if (err == ECANCELED && func == POOL_SCAN_SCRUB &&
1919+
cmd == POOL_SCRUB_NORMAL)
1920+
return (0);
1921+
1922+
if (err == ENOENT && func != POOL_SCAN_NONE && cmd == POOL_SCRUB_NORMAL)
19121923
return (0);
19131924

19141925
if (func == POOL_SCAN_SCRUB) {
1915-
(void) snprintf(msg, sizeof (msg),
1916-
dgettext(TEXT_DOMAIN, "cannot scrub %s"), zc.zc_name);
1926+
if (cmd == POOL_SCRUB_PAUSE) {
1927+
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
1928+
"cannot pause scrubbing %s"), zc.zc_name);
1929+
} else {
1930+
assert(cmd == POOL_SCRUB_NORMAL);
1931+
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
1932+
"cannot scrub %s"), zc.zc_name);
1933+
}
19171934
} else if (func == POOL_SCAN_NONE) {
19181935
(void) snprintf(msg, sizeof (msg),
19191936
dgettext(TEXT_DOMAIN, "cannot cancel scrubbing %s"),
@@ -1922,7 +1939,7 @@ zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func)
19221939
assert(!"unexpected result");
19231940
}
19241941

1925-
if (errno == EBUSY) {
1942+
if (err == EBUSY) {
19261943
nvlist_t *nvroot;
19271944
pool_scan_stat_t *ps = NULL;
19281945
uint_t psc;
@@ -1931,14 +1948,18 @@ zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func)
19311948
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
19321949
(void) nvlist_lookup_uint64_array(nvroot,
19331950
ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &psc);
1934-
if (ps && ps->pss_func == POOL_SCAN_SCRUB)
1935-
return (zfs_error(hdl, EZFS_SCRUBBING, msg));
1936-
else
1951+
if (ps && ps->pss_func == POOL_SCAN_SCRUB) {
1952+
if (cmd == POOL_SCRUB_PAUSE)
1953+
return (zfs_error(hdl, EZFS_SCRUB_PAUSED, msg));
1954+
else
1955+
return (zfs_error(hdl, EZFS_SCRUBBING, msg));
1956+
} else {
19371957
return (zfs_error(hdl, EZFS_RESILVERING, msg));
1938-
} else if (errno == ENOENT) {
1958+
}
1959+
} else if (err == ENOENT) {
19391960
return (zfs_error(hdl, EZFS_NO_SCRUB, msg));
19401961
} else {
1941-
return (zpool_standard_error(hdl, errno, msg));
1962+
return (zpool_standard_error(hdl, err, msg));
19421963
}
19431964
}
19441965

lib/libzfs/libzfs_util.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
2525
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
2626
* Copyright 2016 Igor Kozhukhov <[email protected]>
27+
* Copyright (c) 2017 Datto Inc.
2728
*/
2829

2930
/*
@@ -246,6 +247,9 @@ libzfs_error_description(libzfs_handle_t *hdl)
246247
case EZFS_POSTSPLIT_ONLINE:
247248
return (dgettext(TEXT_DOMAIN, "disk was split from this pool "
248249
"into a new one"));
250+
case EZFS_SCRUB_PAUSED:
251+
return (dgettext(TEXT_DOMAIN, "scrub is paused; "
252+
"use 'zpool scrub' to resume"));
249253
case EZFS_SCRUBBING:
250254
return (dgettext(TEXT_DOMAIN, "currently scrubbing; "
251255
"use 'zpool scrub -s' to cancel current scrub"));

0 commit comments

Comments
 (0)