]> git.proxmox.com Git - mirror_zfs.git/blobdiff - lib/libzfs/libzfs_pool.c
Add slow disk diagnosis to ZED
[mirror_zfs.git] / lib / libzfs / libzfs_pool.c
index 85564edfd862de7427b431fbbbc1a904d138943c..402c14a6baee85b7a7a6d4e8ca5f9c1eec374b57 100644 (file)
@@ -29,7 +29,7 @@
  * Copyright (c) 2017, Intel Corporation.
  * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>
  * Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
- * Copyright (c) 2021, Klara Inc.
+ * Copyright (c) 2021, 2023, Klara Inc.
  */
 
 #include <errno.h>
@@ -255,6 +255,7 @@ zpool_get_state_str(zpool_handle_t *zhp)
        if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
                str = gettext("FAULTED");
        } else if (status == ZPOOL_STATUS_IO_FAILURE_WAIT ||
+           status == ZPOOL_STATUS_IO_FAILURE_CONTINUE ||
            status == ZPOOL_STATUS_IO_FAILURE_MMP) {
                str = gettext("SUSPENDED");
        } else {
@@ -3035,6 +3036,9 @@ zpool_vdev_is_interior(const char *name)
        return (B_FALSE);
 }
 
+/*
+ * Lookup the nvlist for a given vdev.
+ */
 nvlist_t *
 zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
     boolean_t *l2cache, boolean_t *log)
@@ -3042,6 +3046,7 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
        char *end;
        nvlist_t *nvroot, *search, *ret;
        uint64_t guid;
+       boolean_t __avail_spare, __l2cache, __log;
 
        search = fnvlist_alloc();
 
@@ -3057,6 +3062,18 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
        nvroot = fnvlist_lookup_nvlist(zhp->zpool_config,
            ZPOOL_CONFIG_VDEV_TREE);
 
+       /*
+        * User can pass NULL for avail_spare, l2cache, and log, but
+        * we still need to provide variables to vdev_to_nvlist_iter(), so
+        * just point them to junk variables here.
+        */
+       if (!avail_spare)
+               avail_spare = &__avail_spare;
+       if (!l2cache)
+               l2cache = &__l2cache;
+       if (!log)
+               log = &__log;
+
        *avail_spare = B_FALSE;
        *l2cache = B_FALSE;
        if (log != NULL)
@@ -3312,21 +3329,23 @@ zpool_vdev_fault(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
 }
 
 /*
- * Mark the given vdev degraded.
+ * Generic set vdev state function
  */
-int
-zpool_vdev_degrade(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
+static int
+zpool_vdev_set_state(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux,
+    vdev_state_t state)
 {
        zfs_cmd_t zc = {"\0"};
        char errbuf[ERRBUFLEN];
        libzfs_handle_t *hdl = zhp->zpool_hdl;
 
        (void) snprintf(errbuf, sizeof (errbuf),
-           dgettext(TEXT_DOMAIN, "cannot degrade %llu"), (u_longlong_t)guid);
+           dgettext(TEXT_DOMAIN, "cannot set %s %llu"),
+           zpool_state_to_name(state, aux), (u_longlong_t)guid);
 
        (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
        zc.zc_guid = guid;
-       zc.zc_cookie = VDEV_STATE_DEGRADED;
+       zc.zc_cookie = state;
        zc.zc_obj = aux;
 
        if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
@@ -3335,6 +3354,27 @@ zpool_vdev_degrade(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
        return (zpool_standard_error(hdl, errno, errbuf));
 }
 
+/*
+ * Mark the given vdev degraded.
+ */
+int
+zpool_vdev_degrade(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
+{
+       return (zpool_vdev_set_state(zhp, guid, aux, VDEV_STATE_DEGRADED));
+}
+
+/*
+ * Mark the given vdev as in a removed state (as if the device does not exist).
+ *
+ * This is different than zpool_vdev_remove() which does a removal of a device
+ * from the pool (but the device does exist).
+ */
+int
+zpool_vdev_set_removed_state(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
+{
+       return (zpool_vdev_set_state(zhp, guid, aux, VDEV_STATE_REMOVED));
+}
+
 /*
  * Returns TRUE if the given nvlist is a vdev that was originally swapped in as
  * a hot spare.
@@ -3377,6 +3417,7 @@ zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk,
        boolean_t avail_spare, l2cache, islog;
        uint64_t val;
        char *newname;
+       const char *type;
        nvlist_t **child;
        uint_t children;
        nvlist_t *config_root;
@@ -3411,6 +3452,14 @@ zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk,
                return (zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf));
        }
 
+       type = fnvlist_lookup_string(tgt, ZPOOL_CONFIG_TYPE);
+       if (strcmp(type, VDEV_TYPE_RAIDZ) == 0 &&
+           zfeature_lookup_guid("org.openzfs:raidz_expansion", NULL) != 0) {
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "the loaded zfs module doesn't support raidz expansion"));
+               return (zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf));
+       }
+
        if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
            &child, &children) != 0 || children != 1) {
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -3478,6 +3527,10 @@ zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk,
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "cannot replace a replacing device"));
                        }
+               } else if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "raidz_expansion feature must be enabled "
+                           "in order to attach a device to raidz"));
                } else {
                        char status[64] = {0};
                        zpool_prop_get_feature(zhp,
@@ -3507,8 +3560,7 @@ zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk,
                break;
 
        case EBUSY:
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy, "
-                   "or device removal is in progress"),
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy"),
                    new_disk);
                (void) zfs_error(hdl, EZFS_BADDEV, errbuf);
                break;
@@ -3539,6 +3591,34 @@ zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk,
                (void) zfs_error(hdl, EZFS_DEVOVERFLOW, errbuf);
                break;
 
+       case ENXIO:
+               /*
+                * The existing raidz vdev has offline children
+                */
+               if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "raidz vdev has devices that are are offline or "
+                           "being replaced"));
+                       (void) zfs_error(hdl, EZFS_BADDEV, errbuf);
+                       break;
+               } else {
+                       (void) zpool_standard_error(hdl, errno, errbuf);
+               }
+               break;
+
+       case EADDRINUSE:
+               /*
+                * The boot reserved area is already being used (FreeBSD)
+                */
+               if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "the reserved boot area needed for the expansion "
+                           "is already being used by a boot loader"));
+                       (void) zfs_error(hdl, EZFS_BADDEV, errbuf);
+               } else {
+                       (void) zpool_standard_error(hdl, errno, errbuf);
+               }
+               break;
        default:
                (void) zpool_standard_error(hdl, errno, errbuf);
        }
@@ -5184,6 +5264,8 @@ zpool_get_vdev_prop_value(nvlist_t *nvprop, vdev_prop_t prop, char *prop_name,
                case VDEV_PROP_CHECKSUM_T:
                case VDEV_PROP_IO_N:
                case VDEV_PROP_IO_T:
+               case VDEV_PROP_SLOW_IO_N:
+               case VDEV_PROP_SLOW_IO_T:
                        if (intval == UINT64_MAX) {
                                (void) strlcpy(buf, "-", len);
                        } else {
@@ -5221,6 +5303,9 @@ zpool_get_vdev_prop_value(nvlist_t *nvprop, vdev_prop_t prop, char *prop_name,
                } else {
                        src = ZPROP_SRC_DEFAULT;
                        intval = vdev_prop_default_numeric(prop);
+                       /* Only use if provided by the RAIDZ VDEV above */
+                       if (prop == VDEV_PROP_RAIDZ_EXPANDING)
+                               return (ENOENT);
                }
                if (vdev_prop_index_to_string(prop, intval,
                    (const char **)&strval) != 0)