* 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>
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 {
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)
char *end;
nvlist_t *nvroot, *search, *ret;
uint64_t guid;
+ boolean_t __avail_spare, __l2cache, __log;
search = fnvlist_alloc();
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)
}
/*
- * 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)
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.
boolean_t avail_spare, l2cache, islog;
uint64_t val;
char *newname;
+ const char *type;
nvlist_t **child;
uint_t children;
nvlist_t *config_root;
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,
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,
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;
(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);
}
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 {
} 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)