]> git.proxmox.com Git - mirror_zfs.git/blobdiff - lib/libzfs/libzfs_pool.c
Teach zpool scrub to scrub only blocks in error log
[mirror_zfs.git] / lib / libzfs / libzfs_pool.c
index a71cb24736a9499b1ff4e5a9c016f42e805e946c..d4af31c50cf8c0d986eb8d21cee14b5a7d845b50 100644 (file)
@@ -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);
+
+       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;
@@ -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) {