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);
+
+ err = lzc_scrub(ZFS_IOC_POOL_SCRUB, zhp->zpool_name, args, NULL);
+ fnvlist_free(args);
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0)
+ 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);
+ }
+
+ ASSERT3U(func, >=, POOL_SCAN_NONE);
+ ASSERT3U(func, <, POOL_SCAN_FUNCS);
- if (func == POOL_SCAN_SCRUB) {
+ 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;
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) {