mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-23 19:04:45 +03:00
Teach zpool scrub to scrub only blocks in error log
Added a flag '-e' in zpool scrub to scrub only blocks in error log. A user can pause, resume and cancel the error scrub by passing additional command line arguments -p -s just like a regular scrub. This involves adding a new flag, creating new libzfs interfaces, a new ioctl, and the actual iteration and read-issuing logic. Error scrubbing is executed in multiple txg to make sure pool performance is not affected. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Tony Hutter <hutter2@llnl.gov> Co-authored-by: TulsiJain tulsi.jain@delphix.com Signed-off-by: George Amanakis <gamanakis@gmail.com> Closes #8995 Closes #12355
This commit is contained in:
committed by
Brian Behlendorf
parent
e34e15ed6d
commit
482eeef804
@@ -5717,7 +5717,8 @@
|
||||
<enumerator name='POOL_SCAN_NONE' value='0'/>
|
||||
<enumerator name='POOL_SCAN_SCRUB' value='1'/>
|
||||
<enumerator name='POOL_SCAN_RESILVER' value='2'/>
|
||||
<enumerator name='POOL_SCAN_FUNCS' value='3'/>
|
||||
<enumerator name='POOL_SCAN_ERRORSCRUB' value='3'/>
|
||||
<enumerator name='POOL_SCAN_FUNCS' value='4'/>
|
||||
</enum-decl>
|
||||
<typedef-decl name='pool_scan_func_t' type-id='1b092565' id='7313fbe2'/>
|
||||
<enum-decl name='pool_scrub_cmd' id='a1474cbd'>
|
||||
|
||||
+85
-20
@@ -2648,50 +2648,84 @@ out:
|
||||
int
|
||||
zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func, pool_scrub_cmd_t cmd)
|
||||
{
|
||||
zfs_cmd_t zc = {"\0"};
|
||||
char errbuf[ERRBUFLEN];
|
||||
int err;
|
||||
libzfs_handle_t *hdl = zhp->zpool_hdl;
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
|
||||
zc.zc_cookie = func;
|
||||
zc.zc_flags = cmd;
|
||||
nvlist_t *args = fnvlist_alloc();
|
||||
fnvlist_add_uint64(args, "scan_type", (uint64_t)func);
|
||||
fnvlist_add_uint64(args, "scan_command", (uint64_t)cmd);
|
||||
|
||||
if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0)
|
||||
err = lzc_scrub(ZFS_IOC_POOL_SCRUB, zhp->zpool_name, args, NULL);
|
||||
fnvlist_free(args);
|
||||
|
||||
if (err == 0) {
|
||||
return (0);
|
||||
} else if (err == ZFS_ERR_IOC_CMD_UNAVAIL) {
|
||||
zfs_cmd_t zc = {"\0"};
|
||||
(void) strlcpy(zc.zc_name, zhp->zpool_name,
|
||||
sizeof (zc.zc_name));
|
||||
zc.zc_cookie = func;
|
||||
zc.zc_flags = cmd;
|
||||
|
||||
err = errno;
|
||||
if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* ECANCELED on a scrub means we resumed a paused scrub */
|
||||
if (err == ECANCELED && func == POOL_SCAN_SCRUB &&
|
||||
cmd == POOL_SCRUB_NORMAL)
|
||||
/*
|
||||
* An ECANCELED on a scrub means one of the following:
|
||||
* 1. we resumed a paused scrub.
|
||||
* 2. we resumed a paused error scrub.
|
||||
* 3. Error scrub is not run because of no error log.
|
||||
*/
|
||||
if (err == ECANCELED && (func == POOL_SCAN_SCRUB ||
|
||||
func == POOL_SCAN_ERRORSCRUB) && cmd == POOL_SCRUB_NORMAL)
|
||||
return (0);
|
||||
|
||||
if (err == ENOENT && func != POOL_SCAN_NONE && cmd == POOL_SCRUB_NORMAL)
|
||||
/*
|
||||
* The following cases have been handled here:
|
||||
* 1. Paused a scrub/error scrub if there is none in progress.
|
||||
*/
|
||||
if (err == ENOENT && func != POOL_SCAN_NONE && cmd ==
|
||||
POOL_SCRUB_PAUSE) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (func == POOL_SCAN_SCRUB) {
|
||||
ASSERT3U(func, >=, POOL_SCAN_NONE);
|
||||
ASSERT3U(func, <, POOL_SCAN_FUNCS);
|
||||
|
||||
if (func == POOL_SCAN_SCRUB || func == POOL_SCAN_ERRORSCRUB) {
|
||||
if (cmd == POOL_SCRUB_PAUSE) {
|
||||
(void) snprintf(errbuf, sizeof (errbuf),
|
||||
dgettext(TEXT_DOMAIN, "cannot pause scrubbing %s"),
|
||||
zc.zc_name);
|
||||
zhp->zpool_name);
|
||||
} else {
|
||||
assert(cmd == POOL_SCRUB_NORMAL);
|
||||
(void) snprintf(errbuf, sizeof (errbuf),
|
||||
dgettext(TEXT_DOMAIN, "cannot scrub %s"),
|
||||
zc.zc_name);
|
||||
zhp->zpool_name);
|
||||
}
|
||||
} else if (func == POOL_SCAN_RESILVER) {
|
||||
assert(cmd == POOL_SCRUB_NORMAL);
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot restart resilver on %s"), zc.zc_name);
|
||||
"cannot restart resilver on %s"), zhp->zpool_name);
|
||||
} else if (func == POOL_SCAN_NONE) {
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot cancel scrubbing %s"), zc.zc_name);
|
||||
"cannot cancel scrubbing %s"), zhp->zpool_name);
|
||||
} else {
|
||||
assert(!"unexpected result");
|
||||
}
|
||||
|
||||
/*
|
||||
* With EBUSY, five cases are possible:
|
||||
*
|
||||
* Current state Requested
|
||||
* 1. Normal Scrub Running Normal Scrub or Error Scrub
|
||||
* 2. Normal Scrub Paused Error Scrub
|
||||
* 3. Normal Scrub Paused Pause Normal Scrub
|
||||
* 4. Error Scrub Running Normal Scrub or Error Scrub
|
||||
* 5. Error Scrub Paused Pause Error Scrub
|
||||
* 6. Resilvering Anything else
|
||||
*/
|
||||
if (err == EBUSY) {
|
||||
nvlist_t *nvroot;
|
||||
pool_scan_stat_t *ps = NULL;
|
||||
@@ -2703,12 +2737,43 @@ zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func, pool_scrub_cmd_t cmd)
|
||||
ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &psc);
|
||||
if (ps && ps->pss_func == POOL_SCAN_SCRUB &&
|
||||
ps->pss_state == DSS_SCANNING) {
|
||||
if (cmd == POOL_SCRUB_PAUSE)
|
||||
return (zfs_error(hdl, EZFS_SCRUB_PAUSED,
|
||||
if (ps->pss_pass_scrub_pause == 0) {
|
||||
/* handles case 1 */
|
||||
assert(cmd == POOL_SCRUB_NORMAL);
|
||||
return (zfs_error(hdl, EZFS_SCRUBBING,
|
||||
errbuf));
|
||||
else
|
||||
return (zfs_error(hdl, EZFS_SCRUBBING, errbuf));
|
||||
} else {
|
||||
if (func == POOL_SCAN_ERRORSCRUB) {
|
||||
/* handles case 2 */
|
||||
ASSERT3U(cmd, ==, POOL_SCRUB_NORMAL);
|
||||
return (zfs_error(hdl,
|
||||
EZFS_SCRUB_PAUSED_TO_CANCEL,
|
||||
errbuf));
|
||||
} else {
|
||||
/* handles case 3 */
|
||||
ASSERT3U(func, ==, POOL_SCAN_SCRUB);
|
||||
ASSERT3U(cmd, ==, POOL_SCRUB_PAUSE);
|
||||
return (zfs_error(hdl,
|
||||
EZFS_SCRUB_PAUSED, errbuf));
|
||||
}
|
||||
}
|
||||
} else if (ps &&
|
||||
ps->pss_error_scrub_func == POOL_SCAN_ERRORSCRUB &&
|
||||
ps->pss_error_scrub_state == DSS_ERRORSCRUBBING) {
|
||||
if (ps->pss_pass_error_scrub_pause == 0) {
|
||||
/* handles case 4 */
|
||||
ASSERT3U(cmd, ==, POOL_SCRUB_NORMAL);
|
||||
return (zfs_error(hdl, EZFS_ERRORSCRUBBING,
|
||||
errbuf));
|
||||
} else {
|
||||
/* handles case 5 */
|
||||
ASSERT3U(func, ==, POOL_SCAN_ERRORSCRUB);
|
||||
ASSERT3U(cmd, ==, POOL_SCRUB_PAUSE);
|
||||
return (zfs_error(hdl, EZFS_ERRORSCRUB_PAUSED,
|
||||
errbuf));
|
||||
}
|
||||
} else {
|
||||
/* handles case 6 */
|
||||
return (zfs_error(hdl, EZFS_RESILVERING, errbuf));
|
||||
}
|
||||
} else if (err == ENOENT) {
|
||||
|
||||
@@ -243,10 +243,20 @@ libzfs_error_description(libzfs_handle_t *hdl)
|
||||
"into a new one"));
|
||||
case EZFS_SCRUB_PAUSED:
|
||||
return (dgettext(TEXT_DOMAIN, "scrub is paused; "
|
||||
"use 'zpool scrub' to resume"));
|
||||
"use 'zpool scrub' to resume scrub"));
|
||||
case EZFS_SCRUB_PAUSED_TO_CANCEL:
|
||||
return (dgettext(TEXT_DOMAIN, "scrub is paused; "
|
||||
"use 'zpool scrub' to resume or 'zpool scrub -s' to "
|
||||
"cancel scrub"));
|
||||
case EZFS_SCRUBBING:
|
||||
return (dgettext(TEXT_DOMAIN, "currently scrubbing; "
|
||||
"use 'zpool scrub -s' to cancel current scrub"));
|
||||
"use 'zpool scrub -s' to cancel scrub"));
|
||||
case EZFS_ERRORSCRUBBING:
|
||||
return (dgettext(TEXT_DOMAIN, "currently error scrubbing; "
|
||||
"use 'zpool scrub -s' to cancel error scrub"));
|
||||
case EZFS_ERRORSCRUB_PAUSED:
|
||||
return (dgettext(TEXT_DOMAIN, "error scrub is paused; "
|
||||
"use 'zpool scrub -e' to resume error scrub"));
|
||||
case EZFS_NO_SCRUB:
|
||||
return (dgettext(TEXT_DOMAIN, "there is no active scrub"));
|
||||
case EZFS_DIFF:
|
||||
|
||||
Reference in New Issue
Block a user