Print this page
NEX-14252 zpool remove incorrect message: special device contains metadata
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-5736 implement autoreplace matching based on FRU slot number
NEX-6200 hot spares are not reactivated after reinserting into enclosure
NEX-9403 need to update FRU for spare and l2cache devices
NEX-9404 remove lofi autoreplace support from syseventd
NEX-9409 hotsparing doesn't work for vdevs without FRU
NEX-9424 zfs`vdev_online() needs better notification about state changes
Portions contributed by: Alek Pinchuk <alek@nexenta.com>
Portions contributed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-5795 Rename 'wrc' as 'wbc' in the source and in the tech docs
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
NEX-5163 backport illumos 6027 EOL zulu (XVR-4000)
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
6027 EOL zulu (XVR-4000)
Reviewed by: Garrett D'Amore <garrett@damore.org>
Reviewed by: Peter Tribble <peter.tribble@gmail.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Approved by: Dan McDonald <danmcd@omniti.com>
NEX-5162 backport illumos 6507 i386 makecontext(3c) needs to 16-byte align the stack
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
6507 i386 makecontext(3c) needs to 16-byte align the stack
Reviewed by: Gordon Ross <gordon.w.ross@gmail.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Approved by: Dan McDonald <danmcd@omniti.com>
NEX-5207 attempt to activate spare cores fmd
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-5019 wrcache activation races vs. 'zpool create -O wrc_mode='
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
NEX-4934 Add capability to remove special vdev
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
6328 Fix cstyle errors in zfs codebase (fix studio)
6328 Fix cstyle errors in zfs codebase
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Alex Reece <alex@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Reviewed by: Jorgen Lundman <lundman@lundman.net>
Approved by: Robert Mustacchi <rm@joyent.com>
6298 zfs_create_008_neg and zpool_create_023_neg need to be updated for large block support
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: John Kennedy <john.kennedy@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
NEX-4582 update wrc test cases for allow to use write back cache per tree of datasets
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
5960 zfs recv should prefetch indirect blocks
5925 zfs receive -o origin=
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
NEX-3984 On-demand TRIM
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Conflicts:
        usr/src/common/zfs/zpool_prop.c
        usr/src/uts/common/sys/fs/zfs.h
NEX-2325 I/O drop to '0' when FC cable unplugged: path_to_devid() unnecessarily invokes Test Unit Ready which can incorrectly invalidate label causing I/Os failures (fix compile)
NEX-2325 I/O drop to '0' when FC cable unplugged: path_to_devid() unnecessarily invokes Test Unit Ready which can incorrectly invalidate label causing I/Os failures
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
NEX-3025 support root pools on EFI labeled disks
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-2997 Memory leaks in libzfs import implementation
Reviewed by: Dan Fields <dan.fields@nexenta.com>
OS-103 handle CoS descriptor persistent references across vdev operations
OS-102 add man page info and tests for vdev/CoS properties and ZFS meta features
OS-80 support for vdev and CoS properties for the new I/O scheduler
OS-95 lint warning introduced by OS-61
Moved closed ZFS files to open repo, changed Makefiles accordingly
Removed unneeded weak symbols
re 13748 added zpool export -c option
zpool export -c command exports specified pool while keeping its latest
configuration in the cache file for subsequent zpool import -c.
re #12585 rb4049 ZFS++ work port - refactoring to improve separation of open/closed code, bug fixes, performance improvements - open code
re #8279 rb3915 need a mechanism to notify NMS about ZFS config changes (Opened code)
Bug 11205: add missing libzfs_closed_stubs.c to fix opensource-only build.
ZFS plus work: special vdevs, cos, cos/vdev properties

*** 19,32 **** * CDDL HEADER END */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright (c) 2011, 2017 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. - * Copyright 2016 Nexenta Systems, Inc. * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> * Copyright (c) 2017 Datto Inc. */ #include <ctype.h> #include <errno.h> --- 19,32 ---- * CDDL HEADER END */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> + * Copyright 2017 Nexenta Systems, Inc. * Copyright (c) 2017 Datto Inc. */ #include <ctype.h> #include <errno.h>
*** 48,58 **** #include "libzfs_impl.h" #include "zfs_comutil.h" #include "zfeature_common.h" static int read_efi_label(nvlist_t *, diskaddr_t *, boolean_t *); - static boolean_t zpool_vdev_is_interior(const char *name); #define BACKUP_SLICE "s2" typedef struct prop_flags { int create:1; /* Validate property on creation */ --- 48,57 ----
*** 422,432 **** * correct, and parse any numeric properties (index, boolean, etc) if they are * specified as strings. */ static nvlist_t * zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, ! nvlist_t *props, uint64_t version, prop_flags_t flags, char *errbuf) { nvpair_t *elem; nvlist_t *retprops; zpool_prop_t prop; char *strval; --- 421,431 ---- * correct, and parse any numeric properties (index, boolean, etc) if they are * specified as strings. */ static nvlist_t * zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, ! nvlist_t *props, uint64_t version, prop_flags_t flags, const char *errbuf) { nvpair_t *elem; nvlist_t *retprops; zpool_prop_t prop; char *strval;
*** 443,453 **** elem = NULL; while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { const char *propname = nvpair_name(elem); prop = zpool_name_to_prop(propname); ! if (prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname)) { int err; char *fname = strchr(propname, '@') + 1; err = zfeature_lookup_name(fname, NULL); if (err != 0) { --- 442,452 ---- elem = NULL; while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { const char *propname = nvpair_name(elem); prop = zpool_name_to_prop(propname); ! if (prop == ZPROP_INVAL && zpool_prop_feature(propname)) { int err; char *fname = strchr(propname, '@') + 1; err = zfeature_lookup_name(fname, NULL); if (err != 0) {
*** 482,492 **** } /* * Make sure this property is valid and applies to this type. */ ! if (prop == ZPOOL_PROP_INVAL) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid property '%s'"), propname); (void) zfs_error(hdl, EZFS_BADPROP, errbuf); goto error; } --- 481,491 ---- } /* * Make sure this property is valid and applies to this type. */ ! if (prop == ZPROP_INVAL) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid property '%s'"), propname); (void) zfs_error(hdl, EZFS_BADPROP, errbuf); goto error; }
*** 722,732 **** --- 721,783 ---- (void) zpool_props_refresh(zhp); return (ret); } + /* + * Set zpool properties nvlist + */ int + zpool_set_proplist(zpool_handle_t *zhp, nvlist_t *nvl) + { + zfs_cmd_t zc = { 0 }; + int ret = -1; + char errbuf[1024]; + nvlist_t *realprops; + uint64_t version; + prop_flags_t flags = { 0 }; + + assert(nvl != NULL); + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), + zhp->zpool_name); + + version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); + if ((realprops = zpool_valid_proplist(zhp->zpool_hdl, + zhp->zpool_name, nvl, version, flags, errbuf)) == NULL) { + nvlist_free(nvl); + return (-1); + } + + nvlist_free(nvl); + nvl = realprops; + + /* + * Execute the corresponding ioctl() to set this property. + */ + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + + if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl) != 0) { + nvlist_free(nvl); + return (-1); + } + + ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_SET_PROPS, &zc); + + zcmd_free_nvlists(&zc); + nvlist_free(nvl); + + if (ret) + (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf); + else + (void) zpool_props_refresh(zhp); + + return (ret); + } + + int zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp) { libzfs_handle_t *hdl = zhp->zpool_hdl; zprop_list_t *entry; char buf[ZFS_MAXPROPLEN];
*** 1115,1124 **** --- 1166,1176 ---- zfs_cmd_t zc = { 0 }; nvlist_t *zc_fsprops = NULL; nvlist_t *zc_props = NULL; char msg[1024]; int ret = -1; + boolean_t wbc_mode_prop_exists = B_FALSE; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot create '%s'"), pool); if (!zpool_name_valid(hdl, B_FALSE, pool))
*** 1146,1155 **** --- 1198,1213 ---- if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM, fsprops, zoned, NULL, NULL, msg)) == NULL) { goto create_failed; } + + if (nvlist_exists(zc_fsprops, + zfs_prop_to_name(ZFS_PROP_WBC_MODE))) { + wbc_mode_prop_exists = B_TRUE; + } + if (!zc_props && (nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) { goto create_failed; } if (nvlist_add_nvlist(zc_props,
*** 1222,1231 **** --- 1280,1296 ---- case ENOTBLK: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cache device must be a disk or disk slice")); return (zfs_error(hdl, EZFS_BADDEV, msg)); + case EALREADY: + if (wbc_mode_prop_exists) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "WBC is already in the OFF state")); + return (zfs_error(hdl, EZFS_WBCALREADY, msg)); + } + default: return (zpool_standard_error(hdl, errno, msg)); } }
*** 1331,1347 **** zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "one or more vdevs refer to the same device")); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; - case EINVAL: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "invalid config; a pool with removing/removed " - "vdevs does not support adding raidz vdevs")); - (void) zfs_error(hdl, EZFS_BADDEV, msg); - break; - case EOVERFLOW: /* * This occurrs when one of the devices is below * SPA_MINDEVSIZE. Unfortunately, we can't detect which * device was the problem device since there's no --- 1396,1405 ----
*** 1396,1406 **** * Exports the pool from the system. The caller must ensure that there are no * mounted datasets in the pool. */ static int zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce, ! const char *log_str) { zfs_cmd_t zc = { 0 }; char msg[1024]; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, --- 1454,1464 ---- * Exports the pool from the system. The caller must ensure that there are no * mounted datasets in the pool. */ static int zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce, ! boolean_t saveconfig, const char *log_str) { zfs_cmd_t zc = { 0 }; char msg[1024]; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
*** 1407,1416 **** --- 1465,1475 ---- "cannot export '%s'"), zhp->zpool_name); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); zc.zc_cookie = force; zc.zc_guid = hardforce; + zc.zc_obj = saveconfig; zc.zc_history = (uint64_t)(uintptr_t)log_str; if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0) { switch (errno) { case EXDEV:
*** 1429,1447 **** return (0); } int ! zpool_export(zpool_handle_t *zhp, boolean_t force, const char *log_str) { ! return (zpool_export_common(zhp, force, B_FALSE, log_str)); } int ! zpool_export_force(zpool_handle_t *zhp, const char *log_str) { ! return (zpool_export_common(zhp, B_TRUE, B_TRUE, log_str)); } static void zpool_rewind_exclaim(libzfs_handle_t *hdl, const char *name, boolean_t dryrun, nvlist_t *config) --- 1488,1508 ---- return (0); } int ! zpool_export(zpool_handle_t *zhp, boolean_t force, boolean_t saveconfig, ! const char *log_str) { ! return (zpool_export_common(zhp, force, B_FALSE, saveconfig, log_str)); } int ! zpool_export_force(zpool_handle_t *zhp, boolean_t saveconfig, ! const char *log_str) { ! return (zpool_export_common(zhp, B_TRUE, B_TRUE, saveconfig, log_str)); } static void zpool_rewind_exclaim(libzfs_handle_t *hdl, const char *name, boolean_t dryrun, nvlist_t *config)
*** 1806,1818 **** if (nv && nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 && nvlist_lookup_nvlist(nvinfo, ZPOOL_CONFIG_MISSING_DEVICES, &missing) == 0) { (void) printf(dgettext(TEXT_DOMAIN, ! "The devices below are missing or " ! "corrupted, use '-m' to import the pool " ! "anyway:\n")); print_vdev_tree(hdl, NULL, missing, 2); (void) printf("\n"); } (void) zpool_standard_error(hdl, error, desc); break; --- 1867,1878 ---- if (nv && nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 && nvlist_lookup_nvlist(nvinfo, ZPOOL_CONFIG_MISSING_DEVICES, &missing) == 0) { (void) printf(dgettext(TEXT_DOMAIN, ! "The devices below are missing, use " ! "'-m' to import the pool anyway:\n")); print_vdev_tree(hdl, NULL, missing, 2); (void) printf("\n"); } (void) zpool_standard_error(hdl, error, desc); break;
*** 1925,1934 **** --- 1985,2014 ---- return (zpool_standard_error(hdl, err, msg)); } } /* + * Trim the pool. + */ + int + zpool_trim(zpool_handle_t *zhp, boolean_t start, uint64_t rate) + { + zfs_cmd_t zc = { 0 }; + char msg[1024]; + libzfs_handle_t *hdl = zhp->zpool_hdl; + trim_cmd_info_t tci = { .tci_start = start, .tci_rate = rate }; + + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + zc.zc_cookie = (uintptr_t)&tci; + + if (zfs_ioctl(hdl, ZFS_IOC_POOL_TRIM, &zc) == 0) + return (0); + + return (zpool_standard_error(hdl, errno, msg)); + } + + /* * This provides a very minimal check whether a given string is likely a * c#t#d# style string. Users of this are expected to do their own * verification of the s# part. */ #define CTD_CHECK(str) (str && str[0] == 'c' && isdigit(str[1]))
*** 1965,1980 **** * 'avail_spare' is set to TRUE if the provided guid refers to an AVAIL * spare; but FALSE if its an INUSE spare. */ static nvlist_t * vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, ! boolean_t *l2cache, boolean_t *log) { uint_t c, children; nvlist_t **child; nvlist_t *ret; ! uint64_t is_log; char *srchkey; nvpair_t *pair = nvlist_next_nvpair(search, NULL); /* Nothing to look for */ if (search == NULL || pair == NULL) --- 2045,2060 ---- * 'avail_spare' is set to TRUE if the provided guid refers to an AVAIL * spare; but FALSE if its an INUSE spare. */ static nvlist_t * vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, ! boolean_t *l2cache, boolean_t *log, boolean_t *special) { uint_t c, children; nvlist_t **child; nvlist_t *ret; ! uint64_t is_log, is_special; char *srchkey; nvpair_t *pair = nvlist_next_nvpair(search, NULL); /* Nothing to look for */ if (search == NULL || pair == NULL)
*** 2079,2089 **** if (strncmp(val, type, strlen(val)) != 0) { free(type); break; } ! verify(zpool_vdev_is_interior(type)); verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID, &id) == 0); errno = 0; vdev_id = strtoull(idx, &end, 10); --- 2159,2172 ---- if (strncmp(val, type, strlen(val)) != 0) { free(type); break; } ! verify(strncmp(type, VDEV_TYPE_RAIDZ, ! strlen(VDEV_TYPE_RAIDZ)) == 0 || ! strncmp(type, VDEV_TYPE_MIRROR, ! strlen(VDEV_TYPE_MIRROR)) == 0); verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID, &id) == 0); errno = 0; vdev_id = strtoull(idx, &end, 10);
*** 2115,2125 **** &child, &children) != 0) return (NULL); for (c = 0; c < children; c++) { if ((ret = vdev_to_nvlist_iter(child[c], search, ! avail_spare, l2cache, NULL)) != NULL) { /* * The 'is_log' value is only set for the toplevel * vdev, not the leaf vdevs. So we always lookup the * log device from the root of the vdev tree (where * 'log' is non-NULL). --- 2198,2208 ---- &child, &children) != 0) return (NULL); for (c = 0; c < children; c++) { if ((ret = vdev_to_nvlist_iter(child[c], search, ! avail_spare, l2cache, NULL, NULL)) != NULL) { /* * The 'is_log' value is only set for the toplevel * vdev, not the leaf vdevs. So we always lookup the * log device from the root of the vdev tree (where * 'log' is non-NULL).
*** 2128,2146 **** nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &is_log) == 0 && is_log) { *log = B_TRUE; } return (ret); } } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child, &children) == 0) { for (c = 0; c < children; c++) { if ((ret = vdev_to_nvlist_iter(child[c], search, ! avail_spare, l2cache, NULL)) != NULL) { *avail_spare = B_TRUE; return (ret); } } } --- 2211,2237 ---- nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &is_log) == 0 && is_log) { *log = B_TRUE; } + + if (special != NULL && + nvlist_lookup_uint64(child[c], + ZPOOL_CONFIG_IS_SPECIAL, &is_special) == 0 && + is_special) { + *special = B_TRUE; + } + return (ret); } } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child, &children) == 0) { for (c = 0; c < children; c++) { if ((ret = vdev_to_nvlist_iter(child[c], search, ! avail_spare, l2cache, NULL, NULL)) != NULL) { *avail_spare = B_TRUE; return (ret); } } }
*** 2147,2157 **** if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, &child, &children) == 0) { for (c = 0; c < children; c++) { if ((ret = vdev_to_nvlist_iter(child[c], search, ! avail_spare, l2cache, NULL)) != NULL) { *l2cache = B_TRUE; return (ret); } } } --- 2238,2248 ---- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, &child, &children) == 0) { for (c = 0; c < children; c++) { if ((ret = vdev_to_nvlist_iter(child[c], search, ! avail_spare, l2cache, NULL, NULL)) != NULL) { *l2cache = B_TRUE; return (ret); } } }
*** 2177,2210 **** *avail_spare = B_FALSE; *l2cache = B_FALSE; if (log != NULL) *log = B_FALSE; ! ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log); nvlist_free(search); return (ret); } /* * Determine if we have an "interior" top-level vdev (i.e mirror/raidz). */ ! static boolean_t zpool_vdev_is_interior(const char *name) { if (strncmp(name, VDEV_TYPE_RAIDZ, strlen(VDEV_TYPE_RAIDZ)) == 0 || - strncmp(name, VDEV_TYPE_SPARE, strlen(VDEV_TYPE_SPARE)) == 0 || - strncmp(name, - VDEV_TYPE_REPLACING, strlen(VDEV_TYPE_REPLACING)) == 0 || strncmp(name, VDEV_TYPE_MIRROR, strlen(VDEV_TYPE_MIRROR)) == 0) return (B_TRUE); return (B_FALSE); } nvlist_t * zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, ! boolean_t *l2cache, boolean_t *log) { char buf[MAXPATHLEN]; char *end; nvlist_t *nvroot, *search, *ret; uint64_t guid; --- 2268,2299 ---- *avail_spare = B_FALSE; *l2cache = B_FALSE; if (log != NULL) *log = B_FALSE; ! ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log, ! NULL); nvlist_free(search); return (ret); } /* * Determine if we have an "interior" top-level vdev (i.e mirror/raidz). */ ! boolean_t zpool_vdev_is_interior(const char *name) { if (strncmp(name, VDEV_TYPE_RAIDZ, strlen(VDEV_TYPE_RAIDZ)) == 0 || strncmp(name, VDEV_TYPE_MIRROR, strlen(VDEV_TYPE_MIRROR)) == 0) return (B_TRUE); return (B_FALSE); } nvlist_t * zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, ! boolean_t *l2cache, boolean_t *log, boolean_t *special) { char buf[MAXPATHLEN]; char *end; nvlist_t *nvroot, *search, *ret; uint64_t guid;
*** 2229,2239 **** *avail_spare = B_FALSE; *l2cache = B_FALSE; if (log != NULL) *log = B_FALSE; ! ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log); nvlist_free(search); return (ret); } --- 2318,2333 ---- *avail_spare = B_FALSE; *l2cache = B_FALSE; if (log != NULL) *log = B_FALSE; ! ! if (special != NULL) ! *special = B_FALSE; ! ! ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log, ! special); nvlist_free(search); return (ret); }
*** 2447,2464 **** dgettext(TEXT_DOMAIN, "cannot online %s"), path); } (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, ! &islog)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); - if (avail_spare) - return (zfs_error(hdl, EZFS_ISSPARE, msg)); - if ((flags & ZFS_ONLINE_EXPAND || zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) && nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH, &pathname) == 0) { uint64_t wholedisk = 0; --- 2541,2555 ---- dgettext(TEXT_DOMAIN, "cannot online %s"), path); } (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, ! &islog, NULL)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); if ((flags & ZFS_ONLINE_EXPAND || zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) && nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH, &pathname) == 0) { uint64_t wholedisk = 0;
*** 2512,2522 **** (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot offline %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, ! NULL)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); if (avail_spare) --- 2603,2613 ---- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot offline %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, ! NULL, NULL)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); if (avail_spare)
*** 2662,2672 **** (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot attach %s to %s"), new_disk, old_disk); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare, &l2cache, ! &islog)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); if (avail_spare) return (zfs_error(hdl, EZFS_ISSPARE, msg)); --- 2753,2763 ---- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot attach %s to %s"), new_disk, old_disk); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare, &l2cache, ! &islog, NULL)) == 0) return (zfs_error(hdl, EZFS_NODEVICE, msg)); if (avail_spare) return (zfs_error(hdl, EZFS_ISSPARE, msg));
*** 2694,2704 **** * replace it with another hot spare. */ if (replacing && nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_IS_SPARE, &val) == 0 && (zpool_find_vdev(zhp, newname, &avail_spare, &l2cache, ! NULL) == NULL || !avail_spare) && is_replacing_spare(config_root, tgt, 1)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "can only be replaced by another hot spare")); free(newname); return (zfs_error(hdl, EZFS_BADTARGET, msg)); --- 2785,2795 ---- * replace it with another hot spare. */ if (replacing && nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_IS_SPARE, &val) == 0 && (zpool_find_vdev(zhp, newname, &avail_spare, &l2cache, ! NULL, NULL) == NULL || !avail_spare) && is_replacing_spare(config_root, tgt, 1)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "can only be replaced by another hot spare")); free(newname); return (zfs_error(hdl, EZFS_BADTARGET, msg));
*** 2761,2772 **** "new device must be a single disk")); (void) zfs_error(hdl, EZFS_INVALCONFIG, msg); break; case EBUSY: ! zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy, " ! "or pool has removing/removed vdevs"), new_disk); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case EOVERFLOW: --- 2852,2862 ---- "new device must be a single disk")); (void) zfs_error(hdl, EZFS_INVALCONFIG, msg); break; case EBUSY: ! zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy"), new_disk); (void) zfs_error(hdl, EZFS_BADDEV, msg); break; case EOVERFLOW:
*** 2816,2826 **** (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot detach %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, ! NULL)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); if (avail_spare) return (zfs_error(hdl, EZFS_ISSPARE, msg)); --- 2906,2916 ---- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot detach %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, ! NULL, NULL)) == 0) return (zfs_error(hdl, EZFS_NODEVICE, msg)); if (avail_spare) return (zfs_error(hdl, EZFS_ISSPARE, msg));
*** 3105,3223 **** return (0); } /* ! * Remove the given device. */ int zpool_vdev_remove(zpool_handle_t *zhp, const char *path) { zfs_cmd_t zc = { 0 }; char msg[1024]; nvlist_t *tgt; ! boolean_t avail_spare, l2cache, islog; libzfs_handle_t *hdl = zhp->zpool_hdl; uint64_t version; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot remove %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, ! &islog)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); if (islog && version < SPA_VERSION_HOLES) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, ! "pool must be upgraded to support log removal")); return (zfs_error(hdl, EZFS_BADVERSION, msg)); } ! if (!islog && !avail_spare && !l2cache && zpool_is_bootable(zhp)) { ! zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, ! "root pool can not have removed devices, " ! "because GRUB does not understand them")); ! return (zfs_error(hdl, EINVAL, msg)); ! } - zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0) return (0); ! switch (errno) { ! ! case EINVAL: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, ! "invalid config; all top-level vdevs must " ! "have the same sector size and not be raidz.")); ! (void) zfs_error(hdl, EZFS_INVALCONFIG, msg); ! break; ! ! case EBUSY: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, ! "Pool busy; removal may already be in progress")); ! (void) zfs_error(hdl, EZFS_BUSY, msg); ! break; ! ! default: ! (void) zpool_standard_error(hdl, errno, msg); } - return (-1); - } - int - zpool_vdev_remove_cancel(zpool_handle_t *zhp) - { - zfs_cmd_t zc = { 0 }; - char msg[1024]; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(msg, sizeof (msg), - dgettext(TEXT_DOMAIN, "cannot cancel removal")); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_cookie = 1; - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0) - return (0); - return (zpool_standard_error(hdl, errno, msg)); } - int - zpool_vdev_indirect_size(zpool_handle_t *zhp, const char *path, - uint64_t *sizep) - { - char msg[1024]; - nvlist_t *tgt; - boolean_t avail_spare, l2cache, islog; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(msg, sizeof (msg), - dgettext(TEXT_DOMAIN, "cannot determine indirect size of %s"), - path); - - if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, - &islog)) == NULL) - return (zfs_error(hdl, EZFS_NODEVICE, msg)); - - if (avail_spare || l2cache || islog) { - *sizep = 0; - return (0); - } - - if (nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_INDIRECT_SIZE, sizep) != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "indirect size not available")); - return (zfs_error(hdl, EINVAL, msg)); - } - return (0); - } - /* * Clear the errors for the pool, or the particular device if specified. */ int zpool_clear(zpool_handle_t *zhp, const char *path, nvlist_t *rewindnvl) --- 3195,3259 ---- return (0); } /* ! * Remove the given device. Currently, this is supported only for hot spares ! * and level 2 cache devices. */ int zpool_vdev_remove(zpool_handle_t *zhp, const char *path) { zfs_cmd_t zc = { 0 }; char msg[1024]; nvlist_t *tgt; ! boolean_t avail_spare, l2cache, islog, isspecial; libzfs_handle_t *hdl = zhp->zpool_hdl; uint64_t version; (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, "cannot remove %s"), path); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, ! &islog, &isspecial)) == 0) return (zfs_error(hdl, EZFS_NODEVICE, msg)); + /* + * XXX - this should just go away. + */ + if (!avail_spare && !l2cache && !islog && !isspecial) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "only inactive hot spares, cache, top-level, " + "log, or special devices can be removed")); + return (zfs_error(hdl, EZFS_NODEVICE, msg)); + } version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); if (islog && version < SPA_VERSION_HOLES) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, ! "pool must be upgrade to support log removal")); return (zfs_error(hdl, EZFS_BADVERSION, msg)); } ! verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0); if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0) return (0); ! if (isspecial && errno == EEXIST) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, ! "special device contains metadata")); ! return (zfs_error(hdl, EZFS_POOL_NOTSUP, msg)); ! } else if (isspecial && errno == EBUSY) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, ! "wbc feature flag is active")); ! return (zfs_error(hdl, EZFS_WBCCHILD, msg)); } return (zpool_standard_error(hdl, errno, msg)); } /* * Clear the errors for the pool, or the particular device if specified. */ int zpool_clear(zpool_handle_t *zhp, const char *path, nvlist_t *rewindnvl)
*** 3241,3251 **** zhp->zpool_name); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if (path) { if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, ! &l2cache, NULL)) == NULL) return (zfs_error(hdl, EZFS_NODEVICE, msg)); /* * Don't allow error clearing for hot spares. Do allow * error clearing for l2cache devices. --- 3277,3287 ---- zhp->zpool_name); (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); if (path) { if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, ! &l2cache, NULL, NULL)) == 0) return (zfs_error(hdl, EZFS_NODEVICE, msg)); /* * Don't allow error clearing for hot spares. Do allow * error clearing for l2cache devices.
*** 3397,3407 **** { int fd; ddi_devid_t devid; char *minor, *ret; ! if ((fd = open(path, O_RDONLY)) < 0) return (NULL); minor = NULL; ret = NULL; if (devid_get(fd, &devid) == 0) { --- 3433,3443 ---- { int fd; ddi_devid_t devid; char *minor, *ret; ! if ((fd = open(path, O_RDONLY | O_NDELAY)) < 0) return (NULL); minor = NULL; ret = NULL; if (devid_get(fd, &devid) == 0) {
*** 3714,3723 **** --- 3750,3774 ---- nvlist_free(args); zcmd_free_nvlists(&zc); return (err); } + int + zpool_stage_history(libzfs_handle_t *hdl, const char *history_str) + { + if (history_str == NULL) + return (EINVAL); + + if (hdl->libzfs_log_str != NULL) + free(hdl->libzfs_log_str); + + if ((hdl->libzfs_log_str = strdup(history_str)) == NULL) + return (no_memory(hdl)); + + return (0); + } + /* * Perform ioctl to get some command history of a pool. * * 'buf' is the buffer to fill up to 'len' bytes. 'off' is the * logical offset of the history buffer to start reading from.
*** 4284,4288 **** --- 4335,5083 ---- if (zhp) zpool_close(zhp); libzfs_fini(hdl); return (ret); } + + /* + * Vdev props + */ + static int + vdev_get_guid(zpool_handle_t *zhp, const char *path, uint64_t *guid) + { + nvlist_t *nvl; + boolean_t avail_spare, l2cache, islog; + + if ((nvl = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, + &islog, NULL)) == NULL) + return (1); + verify(nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID, guid) == 0); + return (0); + } + + /* + * Given an nvlist of vdev properties to be set, validate that they are + * correct, and parse any numeric properties (index, boolean, etc) if they are + * specified as strings. + */ + /*ARGSUSED*/ + static nvlist_t * + vdev_valid_proplist(libzfs_handle_t *hdl, nvlist_t *props, + uint64_t version, prop_flags_t flags, const char *errbuf) + { + nvpair_t *elem; + nvlist_t *retprops; + vdev_prop_t prop; + char *strval; + uint64_t intval; + + if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) { + (void) no_memory(hdl); + return (NULL); + } + + elem = NULL; + while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { + const char *propname = nvpair_name(elem); + + /* + * Make sure this property is valid and applies to this type. + */ + if ((prop = vdev_name_to_prop(propname)) == ZPROP_INVAL) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid vdev property '%s'"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + + if (vdev_prop_readonly(prop)) { + 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, prop, ZFS_TYPE_VDEV, retprops, + &strval, &intval, errbuf) != 0) + goto error; + + /* + * Perform additional checking for specific properties. + */ + switch (prop) { + case VDEV_PROP_PREFERRED_READ: + if (intval > 100) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be from 0 to 100"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; + } + break; + case VDEV_PROP_READ_MINACTIVE: + case VDEV_PROP_READ_MAXACTIVE: + case VDEV_PROP_AREAD_MINACTIVE: + case VDEV_PROP_AREAD_MAXACTIVE: + case VDEV_PROP_WRITE_MINACTIVE: + case VDEV_PROP_WRITE_MAXACTIVE: + case VDEV_PROP_AWRITE_MINACTIVE: + case VDEV_PROP_AWRITE_MAXACTIVE: + case VDEV_PROP_SCRUB_MINACTIVE: + case VDEV_PROP_SCRUB_MAXACTIVE: + if (intval > 1000) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be from 0 to 1000"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; + } + break; + default: + break; + } + } + + return (retprops); + error: + nvlist_free(retprops); + return (NULL); + } + + static int + vdev_get_all_props(zpool_handle_t *zhp, uint64_t vdev_guid, nvlist_t **nvp) + { + zfs_cmd_t zc = { 0 }; + libzfs_handle_t *hdl = zhp->zpool_hdl; + + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + + if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) + return (-1); + + zc.zc_guid = vdev_guid; + while (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_GET_PROPS, &zc) != 0) { + if (errno == ENOMEM) { + if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { + zcmd_free_nvlists(&zc); + return (-1); + } + } else { + zcmd_free_nvlists(&zc); + (void) zfs_error(hdl, EZFS_BADDEV, + "failed to get vdev properties"); + return (-1); + } + } + + if (zcmd_read_dst_nvlist(hdl, &zc, nvp) != 0) { + zcmd_free_nvlists(&zc); + return (-1); + } + + zcmd_free_nvlists(&zc); + + return (0); + } + + int + vdev_get_prop(zpool_handle_t *zhp, const char *vdev, vdev_prop_t prop, + char *buf, size_t len) + { + uint64_t vdev_guid; + uint64_t intval; + const char *strval; + nvlist_t *nvl; + char errbuf[1024]; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot get property for '%s'"), + zhp->zpool_name); + + if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) + return (-1); + + if (vdev_get_guid(zhp, vdev, &vdev_guid) != 0) { + (void) zfs_error(zhp->zpool_hdl, EZFS_BADDEV, errbuf); + return (-1); + } + + if (vdev_get_all_props(zhp, vdev_guid, &nvl) != 0) + return (-1); + + switch (vdev_prop_get_type(prop)) { + case PROP_TYPE_STRING: + if (nvlist_lookup_string(nvl, vdev_prop_to_name(prop), + (char **)&strval) != 0) + if ((strval = (char *)vdev_prop_default_string(prop)) + == NULL) + strval = "-"; + + (void) strlcpy(buf, strval, len); + break; + + case PROP_TYPE_NUMBER: + if (nvlist_lookup_uint64(nvl, vdev_prop_to_name(prop), + &intval) != 0) + intval = vdev_prop_default_numeric(prop); + (void) snprintf(buf, len, "%llu", (u_longlong_t)intval); + break; + + case PROP_TYPE_INDEX: + if (nvlist_lookup_uint64(nvl, vdev_prop_to_name(prop), + &intval) != 0) + intval = vdev_prop_default_numeric(prop); + if (vdev_prop_index_to_string(prop, intval, &strval) != 0) { + (void) zfs_error(zhp->zpool_hdl, EZFS_BADPROP, errbuf); + nvlist_free(nvl); + return (-1); + } + (void) strlcpy(buf, strval, len); + break; + + default: + abort(); + } + + nvlist_free(nvl); + + return (0); + } + + /* + * Set vdev property : propname=propval. + */ + int + vdev_set_prop(zpool_handle_t *zhp, const char *vdev, + const char *propname, const char *propval) + { + zfs_cmd_t zc = { 0 }; + int ret = -1; + char errbuf[1024]; + nvlist_t *nvl = NULL; + nvlist_t *realprops; + uint64_t version; + uint64_t guid; + prop_flags_t flags = { 0 }; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), + zhp->zpool_name); + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) + return (no_memory(zhp->zpool_hdl)); + + if (nvlist_add_string(nvl, propname, propval) != 0) { + nvlist_free(nvl); + return (no_memory(zhp->zpool_hdl)); + } + + if (vdev_get_guid(zhp, vdev, &guid) != 0) { + (void) zfs_error(zhp->zpool_hdl, EZFS_BADDEV, errbuf); + return (-1); + } + + version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); + if ((realprops = vdev_valid_proplist(zhp->zpool_hdl, nvl, + version, flags, errbuf)) == NULL) { + nvlist_free(nvl); + return (-1); + } + + nvlist_free(nvl); + nvl = realprops; + + /* + * Execute the corresponding ioctl() to set this property. + */ + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + zc.zc_guid = guid; + + if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl) != 0) { + nvlist_free(nvl); + return (-1); + } + + ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SET_PROPS, &zc); + + zcmd_free_nvlists(&zc); + nvlist_free(nvl); + + if (ret) + (void) zpool_vprop_standard_error(zhp->zpool_hdl, + errno, errbuf); + + return (ret); + } + + /* + * Set vdev properties nvlist + */ + int + vdev_set_proplist(zpool_handle_t *zhp, const char *vdev, nvlist_t *nvl) + { + zfs_cmd_t zc = { 0 }; + int ret = -1; + char errbuf[1024]; + nvlist_t *realprops; + uint64_t version; + uint64_t guid; + prop_flags_t flags = { 0 }; + libzfs_handle_t *hdl = zhp->zpool_hdl; + + assert(nvl != NULL); + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), + zhp->zpool_name); + + if (vdev_get_guid(zhp, vdev, &guid) != 0) { + (void) zfs_error(hdl, EZFS_BADDEV, errbuf); + return (-1); + } + + version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); + if ((realprops = vdev_valid_proplist(zhp->zpool_hdl, nvl, + version, flags, errbuf)) == NULL) { + nvlist_free(nvl); + return (-1); + } + + nvlist_free(nvl); + nvl = realprops; + + /* + * Execute the corresponding ioctl() to set this property. + */ + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + zc.zc_guid = guid; + + if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0) { + nvlist_free(nvl); + return (-1); + } + + ret = zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_PROPS, &zc); + + zcmd_free_nvlists(&zc); + nvlist_free(nvl); + + if (ret) + (void) zpool_vprop_standard_error(hdl, errno, errbuf); + + return (ret); + } + + typedef struct vdev_cb { + zpool_handle_t *vcb_zhp; + char *vcb_name; + uint64_t vcb_guid; + boolean_t vcb_is_leaf; + boolean_t vcb_success; + void *vcb_data; + } vdev_cb_t; + + typedef int (*vdev_callback_t)(vdev_cb_t *); + + /* + * Invoke dev_callback for all vdevs in the pool + */ + int + for_each_vdev(zpool_handle_t *zhp, nvlist_t *root, + vdev_callback_t vdev_callback, vdev_cb_t *cb) + { + int ret = 0; + nvlist_t *config, *nvroot, **child, **child_list; + uint32_t children, child_children, c; + libzfs_handle_t *hdl = zhp->zpool_hdl; + + if (!root) { + config = zpool_get_config(zhp, NULL); + verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvroot) == 0); + } else { + nvroot = root; + } + + if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, + &child, &children) != 0) { + (void) fprintf(stderr, gettext("Failed to get the vdev " + "children details from the root nvlist\n")); + return (-1); + } + + cb->vcb_zhp = zhp; + for (c = 0; c < children; c++) { + cb->vcb_is_leaf = B_TRUE; + if (nvlist_lookup_nvlist_array(child[c], ZPOOL_CONFIG_CHILDREN, + &child_list, &child_children) == 0 && + child_children > 0) { + ret = for_each_vdev(zhp, child[c], vdev_callback, cb); + if (ret) + return (ret); + cb->vcb_is_leaf = B_FALSE; + } + + cb->vcb_name = zpool_vdev_name(hdl, NULL, child[c], B_TRUE); + verify(nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_GUID, + &cb->vcb_guid) == 0); + + ret |= vdev_callback(cb); + free(cb->vcb_name); + if (ret) + return (ret); + } + + return (0); + } + + int + get_vdev_guid_callback(vdev_cb_t *cb) + { + if (!cb->vcb_is_leaf) + return (0); + + if (strncmp(cb->vcb_name, (char *)cb->vcb_data, + strlen(cb->vcb_name)) == 0) { + cb->vcb_success = B_TRUE; + } + + return (0); + } + + /* + * Class of Storage (COS) + */ + /*ARGSUSED*/ + static nvlist_t * + cos_valid_proplist(libzfs_handle_t *hdl, nvlist_t *props, + uint64_t version, prop_flags_t flags, const char *errbuf) + { + nvpair_t *elem; + nvlist_t *retprops; + cos_prop_t prop; + char *strval; + uint64_t intval; + + if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) { + (void) no_memory(hdl); + return (NULL); + } + + elem = NULL; + while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { + const char *propname = nvpair_name(elem); + + /* + * Make sure this property is valid and applies to this type. + */ + if ((prop = cos_name_to_prop(propname)) == ZPROP_INVAL) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid cos property '%s'"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + + if (cos_prop_readonly(prop)) { + 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, prop, ZFS_TYPE_COS, retprops, + &strval, &intval, errbuf) != 0) + goto error; + + /* + * Perform additional checking for specific properties. + */ + switch (prop) { + case COS_PROP_PREFERRED_READ: + if (intval > 100) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be from 0 to 100"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; + } + break; + case COS_PROP_READ_MINACTIVE: + case COS_PROP_AREAD_MINACTIVE: + case COS_PROP_WRITE_MINACTIVE: + case COS_PROP_AWRITE_MINACTIVE: + case COS_PROP_SCRUB_MINACTIVE: + case COS_PROP_READ_MAXACTIVE: + case COS_PROP_AREAD_MAXACTIVE: + case COS_PROP_WRITE_MAXACTIVE: + case COS_PROP_AWRITE_MAXACTIVE: + case COS_PROP_SCRUB_MAXACTIVE: + if (intval > 1000) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "'%s' must be from 0 to 1000"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; + } + break; + default: + break; + } + } + + return (retprops); + error: + nvlist_free(retprops); + return (NULL); + } + + int + cos_alloc(zpool_handle_t *zhp, char *cosname, nvlist_t *nvl) + { + zfs_cmd_t zc = { 0 }; + libzfs_handle_t *hdl = zhp->zpool_hdl; + char errbuf[1024]; + int error = 0; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot allocate CoS descriptor for '%s'"), + zhp->zpool_name); + + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + (void) strlcpy(zc.zc_string, cosname, sizeof (zc.zc_string)); + + if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl) != 0) { + nvlist_free(nvl); + return (-1); + } + + error = ioctl(hdl->libzfs_fd, ZFS_IOC_COS_ALLOC, &zc); + + if (error) + (void) zpool_vprop_standard_error(hdl, errno, errbuf); + + return (error); + } + + int + cos_free(zpool_handle_t *zhp, char *cosname, uint64_t guid, boolean_t force) + { + zfs_cmd_t zc = { 0 }; + libzfs_handle_t *hdl = zhp->zpool_hdl; + char errbuf[1024]; + int error = 0; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot free CoS descriptor for '%s'"), + zhp->zpool_name); + + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + (void) strlcpy(zc.zc_string, cosname, sizeof (zc.zc_string)); + zc.zc_guid = guid; + + zc.zc_cookie = (uint64_t)force; + + error = ioctl(hdl->libzfs_fd, ZFS_IOC_COS_FREE, &zc); + + if (error) + (void) zpool_vprop_standard_error(hdl, errno, errbuf); + + return (error); + } + + int + cos_list(zpool_handle_t *zhp, nvlist_t **nvp) + { + zfs_cmd_t zc = { 0 }; + libzfs_handle_t *hdl = zhp->zpool_hdl; + + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + + if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) + return (-1); + + while (ioctl(hdl->libzfs_fd, ZFS_IOC_COS_LIST, &zc) != 0) { + if (errno == ENOMEM) { + if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { + zcmd_free_nvlists(&zc); + return (-1); + } + } else { + zcmd_free_nvlists(&zc); + return (-1); + } + } + + if (zcmd_read_dst_nvlist(hdl, &zc, nvp) != 0) { + zcmd_free_nvlists(&zc); + return (-1); + } + + zcmd_free_nvlists(&zc); + + return (0); + } + + int + cos_get_all_props(zpool_handle_t *zhp, const char *cos, nvlist_t **nvp) + { + uint64_t cos_id; + zfs_cmd_t zc = { 0 }; + libzfs_handle_t *hdl = zhp->zpool_hdl; + char *endp; + char errbuf[1024]; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), + zhp->zpool_name); + + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + + if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) + return (-1); + + cos_id = strtoll(cos, &endp, 10); + zc.zc_guid = cos_id; + if (cos_id == 0 && cos == endp) + (void) strlcpy(zc.zc_string, cos, sizeof (zc.zc_string)); + else + zc.zc_string[0] = '\0'; + + while (ioctl(hdl->libzfs_fd, ZFS_IOC_COS_GET_PROPS, &zc) != 0) { + if (errno == ENOMEM) { + if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { + zcmd_free_nvlists(&zc); + return (-1); + } + } else { + zcmd_free_nvlists(&zc); + (void) zpool_standard_error(hdl, errno, errbuf); + return (-1); + } + } + + if (zcmd_read_dst_nvlist(hdl, &zc, nvp) != 0) { + zcmd_free_nvlists(&zc); + return (-1); + } + + zcmd_free_nvlists(&zc); + + return (0); + } + + int + cos_get_prop(zpool_handle_t *zhp, const char *cos, cos_prop_t prop, + char *buf, size_t len, nvlist_t **nvp) + { + uint64_t intval; + const char *strval; + nvlist_t *nvl; + char errbuf[1024]; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), + zhp->zpool_name); + + assert(nvp != NULL); + + if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) + return (-1); + + if (*nvp == NULL && cos_get_all_props(zhp, cos, nvp) != 0) + return (-1); + nvl = *nvp; + + switch (cos_prop_get_type(prop)) { + case PROP_TYPE_STRING: + if (nvlist_lookup_string(nvl, cos_prop_to_name(prop), + (char **)&strval) != 0) + if ((strval = (char *)cos_prop_default_string(prop)) + == NULL) + strval = "-"; + + (void) strlcpy(buf, strval, len); + break; + + case PROP_TYPE_NUMBER: + if (nvlist_lookup_uint64(nvl, cos_prop_to_name(prop), + &intval) != 0) + intval = cos_prop_default_numeric(prop); + (void) snprintf(buf, len, "%llu", (u_longlong_t)intval); + break; + + case PROP_TYPE_INDEX: + if (nvlist_lookup_uint64(nvl, cos_prop_to_name(prop), + &intval) != 0) + intval = cos_prop_default_numeric(prop); + if (cos_prop_index_to_string(prop, intval, &strval) != 0) { + (void) zfs_error(zhp->zpool_hdl, EZFS_BADPROP, errbuf); + return (-1); + } + (void) strlcpy(buf, strval, len); + break; + + default: + abort(); + } + + return (0); + } + + /* + * Set cos properties nvlist + */ + int + cos_set_proplist(zpool_handle_t *zhp, const char *cos, nvlist_t *nvl) + { + zfs_cmd_t zc = { 0 }; + int ret = -1; + char errbuf[1024]; + char *endp; + nvlist_t *realprops; + uint64_t version; + uint64_t cos_id; + prop_flags_t flags = { 0 }; + + assert(nvl != NULL); + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), + zhp->zpool_name); + + version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); + if ((realprops = cos_valid_proplist(zhp->zpool_hdl, nvl, + version, flags, errbuf)) == NULL) { + nvlist_free(nvl); + return (-1); + } + + nvlist_free(nvl); + nvl = realprops; + + /* + * Execute the corresponding ioctl() to set this property. + */ + (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); + + cos_id = strtoll(cos, &endp, 10); + zc.zc_guid = cos_id; + if (cos_id == 0 && cos == endp) + (void) strlcpy(zc.zc_string, cos, sizeof (zc.zc_string)); + else + zc.zc_string[0] = '\0'; + + if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl) != 0) { + nvlist_free(nvl); + return (-1); + } + + ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_COS_SET_PROPS, &zc); + + zcmd_free_nvlists(&zc); + nvlist_free(nvl); + + if (ret) + (void) zpool_vprop_standard_error_fmt(zhp->zpool_hdl, + errno, errbuf); + + return (ret); + }