aboutsummaryrefslogtreecommitdiffstats
path: root/lib/libzfs/libzfs_pool.c
diff options
context:
space:
mode:
authorAllan Jude <[email protected]>2021-11-30 09:46:25 -0500
committerGitHub <[email protected]>2021-11-30 07:46:25 -0700
commit2a673e76a928cca4df7794cdcaa02e0be149c4da (patch)
tree016afa631bb0d98f55b01e93ab80842fe6ffda27 /lib/libzfs/libzfs_pool.c
parent5f64bf7fdeebfbad50e98c6cd0c3a361a9aecabc (diff)
Vdev Properties Feature
Add properties, similar to pool properties, to each vdev. This makes use of the existing per-vdev ZAP that was added as part of device evacuation/removal. A large number of read-only properties are exposed, many of the members of struct vdev_t, that provide useful statistics. Adds support for read-only "removing" vdev property. Adds the "allocating" property that defaults to "on" and can be set to "off" to prevent future allocations from that top-level vdev. Supports user-defined vdev properties. Includes support for properties.vdev in SYSFS. Co-authored-by: Allan Jude <[email protected]> Co-authored-by: Mark Maybee <[email protected]> Reviewed-by: Matthew Ahrens <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Signed-off-by: Allan Jude <[email protected]> Closes #11711
Diffstat (limited to 'lib/libzfs/libzfs_pool.c')
-rw-r--r--lib/libzfs/libzfs_pool.c459
1 files changed, 457 insertions, 2 deletions
diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c
index 8ed96275c..6e302ad4b 100644
--- a/lib/libzfs/libzfs_pool.c
+++ b/lib/libzfs/libzfs_pool.c
@@ -29,6 +29,7 @@
* Copyright (c) 2017, Intel Corporation.
* Copyright (c) 2018, loli10K <[email protected]>
* Copyright (c) 2021, Colm Buckley <[email protected]>
+ * Copyright (c) 2021, Klara Inc.
*/
#include <errno.h>
@@ -61,6 +62,7 @@ static boolean_t zpool_vdev_is_interior(const char *name);
typedef struct prop_flags {
int create:1; /* Validate property on creation */
int import:1; /* Validate property on import */
+ int vdevprop:1; /* Validate property as a VDEV property */
} prop_flags_t;
/*
@@ -478,6 +480,35 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
const char *propname = nvpair_name(elem);
+ if (flags.vdevprop && zpool_prop_vdev(propname)) {
+ vdev_prop_t vprop = vdev_name_to_prop(propname);
+
+ if (vdev_prop_readonly(vprop)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' "
+ "is readonly"), propname);
+ (void) zfs_error(hdl, EZFS_PROPREADONLY,
+ errbuf);
+ goto error;
+ }
+
+ if (zprop_parse_value(hdl, elem, vprop, ZFS_TYPE_VDEV,
+ retprops, &strval, &intval, errbuf) != 0)
+ goto error;
+
+ continue;
+ } else if (flags.vdevprop && vdev_prop_user(propname)) {
+ if (nvlist_add_nvpair(retprops, elem) != 0) {
+ (void) no_memory(hdl);
+ goto error;
+ }
+ continue;
+ } else if (flags.vdevprop) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid property: '%s'"), propname);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
prop = zpool_name_to_prop(propname);
if (prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname)) {
int err;
@@ -806,7 +837,7 @@ zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval)
int
zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
- boolean_t literal)
+ zfs_type_t type, boolean_t literal)
{
libzfs_handle_t *hdl = zhp->zpool_hdl;
zprop_list_t *entry;
@@ -817,9 +848,12 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
boolean_t firstexpand = (NULL == *plp);
int i;
- if (zprop_expand_list(hdl, plp, ZFS_TYPE_POOL) != 0)
+ if (zprop_expand_list(hdl, plp, type) != 0)
return (-1);
+ if (type == ZFS_TYPE_VDEV)
+ return (0);
+
last = plp;
while (*last != NULL)
last = &(*last)->pl_next;
@@ -899,6 +933,77 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
return (0);
}
+int
+vdev_expand_proplist(zpool_handle_t *zhp, const char *vdevname,
+ zprop_list_t **plp)
+{
+ zprop_list_t *entry;
+ char buf[ZFS_MAXPROPLEN];
+ char *strval = NULL;
+ int err = 0;
+ nvpair_t *elem = NULL;
+ nvlist_t *vprops = NULL;
+ nvlist_t *propval = NULL;
+ const char *propname;
+ vdev_prop_t prop;
+ zprop_list_t **last;
+
+ for (entry = *plp; entry != NULL; entry = entry->pl_next) {
+ if (entry->pl_fixed)
+ continue;
+
+ if (zpool_get_vdev_prop(zhp, vdevname, entry->pl_prop,
+ entry->pl_user_prop, buf, sizeof (buf), NULL,
+ B_FALSE) == 0) {
+ if (strlen(buf) > entry->pl_width)
+ entry->pl_width = strlen(buf);
+ }
+ if (entry->pl_prop == VDEV_PROP_NAME &&
+ strlen(vdevname) > entry->pl_width)
+ entry->pl_width = strlen(vdevname);
+ }
+
+ /* Handle the all properties case */
+ last = plp;
+ if (*last != NULL && (*last)->pl_all == B_TRUE) {
+ while (*last != NULL)
+ last = &(*last)->pl_next;
+
+ err = zpool_get_all_vdev_props(zhp, vdevname, &vprops);
+ if (err != 0)
+ return (err);
+
+ while ((elem = nvlist_next_nvpair(vprops, elem)) != NULL) {
+ propname = nvpair_name(elem);
+
+ /* Skip properties that are not user defined */
+ if ((prop = vdev_name_to_prop(propname)) !=
+ VDEV_PROP_USER)
+ continue;
+
+ if (nvpair_value_nvlist(elem, &propval) != 0)
+ continue;
+
+ verify(nvlist_lookup_string(propval, ZPROP_VALUE,
+ &strval) == 0);
+
+ if ((entry = zfs_alloc(zhp->zpool_hdl,
+ sizeof (zprop_list_t))) == NULL)
+ return (ENOMEM);
+
+ entry->pl_prop = prop;
+ entry->pl_user_prop = zfs_strdup(zhp->zpool_hdl,
+ propname);
+ entry->pl_width = strlen(strval);
+ entry->pl_all = B_TRUE;
+ *last = entry;
+ last = &entry->pl_next;
+ }
+ }
+
+ return (0);
+}
+
/*
* Get the state for the given feature on the given ZFS pool.
*/
@@ -4959,3 +5064,353 @@ zpool_load_compat(const char *compat, boolean_t *features, char *report,
strlcpy(report, gettext("compatibility set ok"), rlen);
return (ZPOOL_COMPATIBILITY_OK);
}
+
+static int
+zpool_vdev_guid(zpool_handle_t *zhp, const char *vdevname, uint64_t *vdev_guid)
+{
+ nvlist_t *tgt;
+ boolean_t avail_spare, l2cache;
+
+ verify(zhp != NULL);
+ if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "pool is in an unavailable state"));
+ return (zfs_error(zhp->zpool_hdl, EZFS_POOLUNAVAIL, errbuf));
+ }
+
+ if ((tgt = zpool_find_vdev(zhp, vdevname, &avail_spare, &l2cache,
+ NULL)) == NULL) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "can not find %s in %s"),
+ vdevname, zhp->zpool_name);
+ return (zfs_error(zhp->zpool_hdl, EZFS_NODEVICE, errbuf));
+ }
+
+ verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, vdev_guid) == 0);
+ return (0);
+}
+
+/*
+ * Get a vdev property value for 'prop' and return the value in
+ * a pre-allocated buffer.
+ */
+int
+zpool_get_vdev_prop_value(nvlist_t *nvprop, vdev_prop_t prop, char *prop_name,
+ char *buf, size_t len, zprop_source_t *srctype, boolean_t literal)
+{
+ nvlist_t *nv;
+ uint64_t intval;
+ char *strval;
+ zprop_source_t src = ZPROP_SRC_NONE;
+
+ if (prop == VDEV_PROP_USER) {
+ /* user property, prop_name must contain the property name */
+ assert(prop_name != NULL);
+ if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) {
+ verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE,
+ &intval) == 0);
+ src = intval;
+ verify(nvlist_lookup_string(nv, ZPROP_VALUE,
+ &strval) == 0);
+ } else {
+ /* user prop not found */
+ return (-1);
+ }
+ (void) strlcpy(buf, strval, len);
+ if (srctype)
+ *srctype = src;
+ return (0);
+ }
+
+ if (prop_name == NULL)
+ prop_name = (char *)vdev_prop_to_name(prop);
+
+ switch (vdev_prop_get_type(prop)) {
+ case PROP_TYPE_STRING:
+ if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) {
+ verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE,
+ &intval) == 0);
+ src = intval;
+ verify(nvlist_lookup_string(nv, ZPROP_VALUE,
+ &strval) == 0);
+ } else {
+ src = ZPROP_SRC_DEFAULT;
+ if ((strval = (char *)vdev_prop_default_string(prop))
+ == NULL)
+ strval = "-";
+ }
+ (void) strlcpy(buf, strval, len);
+ break;
+
+ case PROP_TYPE_NUMBER:
+ if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) {
+ verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE,
+ &intval) == 0);
+ src = intval;
+ verify(nvlist_lookup_uint64(nv, ZPROP_VALUE,
+ &intval) == 0);
+ } else {
+ src = ZPROP_SRC_DEFAULT;
+ intval = vdev_prop_default_numeric(prop);
+ }
+
+ switch (prop) {
+ case VDEV_PROP_ASIZE:
+ case VDEV_PROP_PSIZE:
+ case VDEV_PROP_SIZE:
+ case VDEV_PROP_ALLOCATED:
+ case VDEV_PROP_FREE:
+ case VDEV_PROP_READ_ERRORS:
+ case VDEV_PROP_WRITE_ERRORS:
+ case VDEV_PROP_CHECKSUM_ERRORS:
+ case VDEV_PROP_INITIALIZE_ERRORS:
+ case VDEV_PROP_OPS_NULL:
+ case VDEV_PROP_OPS_READ:
+ case VDEV_PROP_OPS_WRITE:
+ case VDEV_PROP_OPS_FREE:
+ case VDEV_PROP_OPS_CLAIM:
+ case VDEV_PROP_OPS_TRIM:
+ case VDEV_PROP_BYTES_NULL:
+ case VDEV_PROP_BYTES_READ:
+ case VDEV_PROP_BYTES_WRITE:
+ case VDEV_PROP_BYTES_FREE:
+ case VDEV_PROP_BYTES_CLAIM:
+ case VDEV_PROP_BYTES_TRIM:
+ if (literal) {
+ (void) snprintf(buf, len, "%llu",
+ (u_longlong_t)intval);
+ } else {
+ (void) zfs_nicenum(intval, buf, len);
+ }
+ break;
+ case VDEV_PROP_EXPANDSZ:
+ if (intval == 0) {
+ (void) strlcpy(buf, "-", len);
+ } else if (literal) {
+ (void) snprintf(buf, len, "%llu",
+ (u_longlong_t)intval);
+ } else {
+ (void) zfs_nicenum(intval, buf, len);
+ }
+ break;
+ case VDEV_PROP_CAPACITY:
+ if (literal) {
+ (void) snprintf(buf, len, "%llu",
+ (u_longlong_t)intval);
+ } else {
+ (void) snprintf(buf, len, "%llu%%",
+ (u_longlong_t)intval);
+ }
+ break;
+ case VDEV_PROP_FRAGMENTATION:
+ if (intval == UINT64_MAX) {
+ (void) strlcpy(buf, "-", len);
+ } else {
+ (void) snprintf(buf, len, "%llu%%",
+ (u_longlong_t)intval);
+ }
+ break;
+ case VDEV_PROP_STATE:
+ if (literal) {
+ (void) snprintf(buf, len, "%llu",
+ (u_longlong_t)intval);
+ } else {
+ (void) strlcpy(buf, zpool_state_to_name(intval,
+ VDEV_AUX_NONE), len);
+ }
+ break;
+ default:
+ (void) snprintf(buf, len, "%llu",
+ (u_longlong_t)intval);
+ }
+ break;
+
+ case PROP_TYPE_INDEX:
+ if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) {
+ verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE,
+ &intval) == 0);
+ src = intval;
+ verify(nvlist_lookup_uint64(nv, ZPROP_VALUE,
+ &intval) == 0);
+ } else {
+ src = ZPROP_SRC_DEFAULT;
+ intval = vdev_prop_default_numeric(prop);
+ }
+ if (vdev_prop_index_to_string(prop, intval,
+ (const char **)&strval) != 0)
+ return (-1);
+ (void) strlcpy(buf, strval, len);
+ break;
+
+ default:
+ abort();
+ }
+
+ if (srctype)
+ *srctype = src;
+
+ return (0);
+}
+
+/*
+ * Get a vdev property value for 'prop_name' and return the value in
+ * a pre-allocated buffer.
+ */
+int
+zpool_get_vdev_prop(zpool_handle_t *zhp, const char *vdevname, vdev_prop_t prop,
+ char *prop_name, char *buf, size_t len, zprop_source_t *srctype,
+ boolean_t literal)
+{
+ nvlist_t *reqnvl, *reqprops;
+ nvlist_t *retprops = NULL;
+ uint64_t vdev_guid;
+ int ret;
+
+ if ((ret = zpool_vdev_guid(zhp, vdevname, &vdev_guid)) != 0)
+ return (ret);
+
+ if (nvlist_alloc(&reqnvl, NV_UNIQUE_NAME, 0) != 0)
+ return (no_memory(zhp->zpool_hdl));
+ if (nvlist_alloc(&reqprops, NV_UNIQUE_NAME, 0) != 0)
+ return (no_memory(zhp->zpool_hdl));
+
+ fnvlist_add_uint64(reqnvl, ZPOOL_VDEV_PROPS_GET_VDEV, vdev_guid);
+
+ if (prop != VDEV_PROP_USER) {
+ /* prop_name overrides prop value */
+ if (prop_name != NULL)
+ prop = vdev_name_to_prop(prop_name);
+ else
+ prop_name = (char *)vdev_prop_to_name(prop);
+ assert(prop < VDEV_NUM_PROPS);
+ }
+
+ assert(prop_name != NULL);
+ if (nvlist_add_uint64(reqprops, prop_name, prop) != 0) {
+ nvlist_free(reqnvl);
+ nvlist_free(reqprops);
+ return (no_memory(zhp->zpool_hdl));
+ }
+
+ fnvlist_add_nvlist(reqnvl, ZPOOL_VDEV_PROPS_GET_PROPS, reqprops);
+
+ ret = lzc_get_vdev_prop(zhp->zpool_name, reqnvl, &retprops);
+
+ if (ret == 0) {
+ ret = zpool_get_vdev_prop_value(retprops, prop, prop_name, buf,
+ len, srctype, literal);
+ } else {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot get vdev property %s from"
+ " %s in %s"), prop_name, vdevname, zhp->zpool_name);
+ (void) zpool_standard_error(zhp->zpool_hdl, ret, errbuf);
+ }
+
+ nvlist_free(reqnvl);
+ nvlist_free(reqprops);
+ nvlist_free(retprops);
+
+ return (ret);
+}
+
+/*
+ * Get all vdev properties
+ */
+int
+zpool_get_all_vdev_props(zpool_handle_t *zhp, const char *vdevname,
+ nvlist_t **outnvl)
+{
+ nvlist_t *nvl = NULL;
+ uint64_t vdev_guid;
+ int ret;
+
+ if ((ret = zpool_vdev_guid(zhp, vdevname, &vdev_guid)) != 0)
+ return (ret);
+
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+ return (no_memory(zhp->zpool_hdl));
+
+ fnvlist_add_uint64(nvl, ZPOOL_VDEV_PROPS_GET_VDEV, vdev_guid);
+
+ ret = lzc_get_vdev_prop(zhp->zpool_name, nvl, outnvl);
+
+ nvlist_free(nvl);
+
+ if (ret) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot get vdev properties for"
+ " %s in %s"), vdevname, zhp->zpool_name);
+ (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf);
+ }
+
+ return (ret);
+}
+
+/*
+ * Set vdev property
+ */
+int
+zpool_set_vdev_prop(zpool_handle_t *zhp, const char *vdevname,
+ const char *propname, const char *propval)
+{
+ int ret;
+ vdev_prop_t vprop;
+ nvlist_t *nvl = NULL;
+ nvlist_t *outnvl = NULL;
+ nvlist_t *props;
+ nvlist_t *realprops;
+ prop_flags_t flags = { 0 };
+ uint64_t version;
+ uint64_t vdev_guid;
+
+ if ((ret = zpool_vdev_guid(zhp, vdevname, &vdev_guid)) != 0)
+ return (ret);
+
+ vprop = vdev_name_to_prop(propname);
+
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+ return (no_memory(zhp->zpool_hdl));
+ if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
+ return (no_memory(zhp->zpool_hdl));
+
+ fnvlist_add_uint64(nvl, ZPOOL_VDEV_PROPS_SET_VDEV, vdev_guid);
+
+ if (nvlist_add_string(props, propname, propval) != 0) {
+ nvlist_free(props);
+ return (no_memory(zhp->zpool_hdl));
+ }
+
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot set property %s for %s on %s"),
+ propname, vdevname, zhp->zpool_name);
+
+ flags.vdevprop = 1;
+ version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
+ if ((realprops = zpool_valid_proplist(zhp->zpool_hdl,
+ zhp->zpool_name, props, version, flags, errbuf)) == NULL) {
+ nvlist_free(props);
+ nvlist_free(nvl);
+ return (-1);
+ }
+
+ nvlist_free(props);
+ props = realprops;
+
+ fnvlist_add_nvlist(nvl, ZPOOL_VDEV_PROPS_SET_PROPS, props);
+
+ ret = lzc_set_vdev_prop(zhp->zpool_name, nvl, &outnvl);
+
+ nvlist_free(props);
+ nvlist_free(nvl);
+ nvlist_free(outnvl);
+
+ if (ret)
+ (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf);
+
+ return (ret);
+}