diff --git a/cmd/zpool/Makefile.am b/cmd/zpool/Makefile.am index aad45d4f7497..29a8be6d1a30 100644 --- a/cmd/zpool/Makefile.am +++ b/cmd/zpool/Makefile.am @@ -26,7 +26,8 @@ zpool_LDADD = \ $(abs_top_builddir)/lib/libzfs/libzfs.la \ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \ $(abs_top_builddir)/lib/libnvpair/libnvpair.la \ - $(abs_top_builddir)/lib/libuutil/libuutil.la + $(abs_top_builddir)/lib/libuutil/libuutil.la \ + $(abs_top_builddir)/lib/libzfs_internal/libzfs_internal.la zpool_LDADD += $(LTLIBINTL) diff --git a/cmd/zpool/zpool_iter.c b/cmd/zpool/zpool_iter.c index 3d7a0cfc35e6..ac508e7176bf 100644 --- a/cmd/zpool/zpool_iter.c +++ b/cmd/zpool/zpool_iter.c @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -264,72 +265,6 @@ for_each_pool(int argc, char **argv, boolean_t unavail, return (ret); } -static int -for_each_vdev_cb(zpool_handle_t *zhp, nvlist_t *nv, pool_vdev_iter_f func, - void *data) -{ - nvlist_t **child; - uint_t c, children; - int ret = 0; - int i; - char *type; - - const char *list[] = { - ZPOOL_CONFIG_SPARES, - ZPOOL_CONFIG_L2CACHE, - ZPOOL_CONFIG_CHILDREN - }; - - for (i = 0; i < ARRAY_SIZE(list); i++) { - if (nvlist_lookup_nvlist_array(nv, list[i], &child, - &children) == 0) { - for (c = 0; c < children; c++) { - uint64_t ishole = 0; - - (void) nvlist_lookup_uint64(child[c], - ZPOOL_CONFIG_IS_HOLE, &ishole); - - if (ishole) - continue; - - ret |= for_each_vdev_cb(zhp, child[c], func, - data); - } - } - } - - if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0) - return (ret); - - /* Don't run our function on root vdevs */ - if (strcmp(type, VDEV_TYPE_ROOT) != 0) { - ret |= func(zhp, nv, data); - } - - return (ret); -} - -/* - * This is the equivalent of for_each_pool() for vdevs. It iterates thorough - * all vdevs in the pool, ignoring root vdevs and holes, calling func() on - * each one. - * - * @zhp: Zpool handle - * @func: Function to call on each vdev - * @data: Custom data to pass to the function - */ -int -for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data) -{ - nvlist_t *config, *nvroot = NULL; - - if ((config = zpool_get_config(zhp, NULL)) != NULL) { - verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, - &nvroot) == 0); - } - return (for_each_vdev_cb(zhp, nvroot, func, data)); -} - /* * Process the vcdl->vdev_cmd_data[] array to figure out all the unique column * names and their widths. When this function is done, vcdl->uniq_cols, diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index bfef6fc43285..ed3c53f03137 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -70,6 +70,7 @@ #include #include +#include #include "zpool_util.h" #include "zfs_comutil.h" diff --git a/cmd/zpool/zpool_util.h b/cmd/zpool/zpool_util.h index 4002e5794021..734278cad080 100644 --- a/cmd/zpool/zpool_util.h +++ b/cmd/zpool/zpool_util.h @@ -67,10 +67,6 @@ nvlist_t *split_mirror_vdev(zpool_handle_t *zhp, char *newname, int for_each_pool(int, char **, boolean_t unavail, zprop_list_t **, boolean_t, zpool_iter_f, void *); -/* Vdev list functions */ -typedef int (*pool_vdev_iter_f)(zpool_handle_t *, nvlist_t *, void *); -int for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data); - typedef struct zpool_list zpool_list_t; zpool_list_t *pool_list_get(int, char **, zprop_list_t **, boolean_t, int *); diff --git a/configure.ac b/configure.ac index 6f34b210d2b7..058b7f45b317 100644 --- a/configure.ac +++ b/configure.ac @@ -170,6 +170,7 @@ AC_CONFIG_FILES([ lib/libzpool/Makefile lib/libzstd/Makefile lib/libzutil/Makefile + lib/libzfs_internal/Makefile man/Makefile module/Kbuild module/Makefile diff --git a/include/Makefile.am b/include/Makefile.am index 4da43afd850d..a368c311a6e3 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -7,7 +7,8 @@ COMMON_H = \ zfs_deleg.h \ zfs_fletcher.h \ zfs_namecheck.h \ - zfs_prop.h + zfs_prop.h \ + libzfs_internal.h USER_H = \ libnvpair.h \ diff --git a/include/libzfs_internal.h b/include/libzfs_internal.h new file mode 100644 index 000000000000..285c9e565511 --- /dev/null +++ b/include/libzfs_internal.h @@ -0,0 +1,17 @@ +#ifndef _LIBZUTIL_INTERNAL +#define _LIBZUTIL_INTERNAL + +#include + +/* Vdev list functions */ +typedef int (*pool_vdev_iter_f)(zpool_handle_t *, nvlist_t *, void *); +extern int for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, + void *data); +extern int for_each_vdev_in_nvlist(nvlist_t *nvroot, pool_vdev_iter_f func, + void *data); + +/* Only used by libzfs_config.c */ +nvlist_t * +zpool_get_config_impl(zpool_handle_t *zhp, nvlist_t **oldconfig); + +#endif diff --git a/include/libzutil.h b/include/libzutil.h index ef17bd5426df..c9957ecdff31 100644 --- a/include/libzutil.h +++ b/include/libzutil.h @@ -84,7 +84,8 @@ struct udev_device; _LIBZUTIL_H int zfs_device_get_devid(struct udev_device *, char *, size_t); _LIBZUTIL_H int zfs_device_get_physical(struct udev_device *, char *, size_t); -_LIBZUTIL_H void update_vdev_config_dev_strs(nvlist_t *); +_LIBZUTIL_H void update_vdev_config_dev_strs(nvlist_t *nv); +_LIBZUTIL_H void update_vdevs_config_dev_sysfs_path(nvlist_t *config); /* * Default device paths diff --git a/lib/Makefile.am b/lib/Makefile.am index db7a3fa31d40..47d89ad1eacf 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -14,7 +14,7 @@ endif SUBDIRS += libnvpair # libzutil depends on libefi if present -SUBDIRS += libzutil libunicode +SUBDIRS += libzfs_internal libzutil libunicode # These five libraries, which are installed as the final build product, # incorporate the eight convenience libraries given above. diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am index e3527ffe7058..2dfca67e1d2c 100644 --- a/lib/libzfs/Makefile.am +++ b/lib/libzfs/Makefile.am @@ -75,7 +75,9 @@ libzfs_la_LIBADD = \ $(abs_top_builddir)/lib/libshare/libshare.la \ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \ $(abs_top_builddir)/lib/libnvpair/libnvpair.la \ - $(abs_top_builddir)/lib/libuutil/libuutil.la + $(abs_top_builddir)/lib/libuutil/libuutil.la \ + $(abs_top_builddir)/lib/libzutil/libzutil.la \ + $(abs_top_builddir)/lib/libzfs_internal/libzfs_internal.la libzfs_la_LIBADD += -lm $(LIBCRYPTO_LIBS) $(ZLIB_LIBS) $(LIBFETCH_LIBS) $(LTLIBINTL) diff --git a/lib/libzfs/libzfs_config.c b/lib/libzfs/libzfs_config.c index a3ecc4a327dc..c3805e920eab 100644 --- a/lib/libzfs/libzfs_config.c +++ b/lib/libzfs/libzfs_config.c @@ -47,6 +47,7 @@ #include #include #include +#include #include "libzfs_impl.h" @@ -219,9 +220,7 @@ namespace_reload(libzfs_handle_t *hdl) nvlist_t * zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig) { - if (oldconfig) - *oldconfig = zhp->zpool_old_config; - return (zhp->zpool_config); + return (zpool_get_config_impl(zhp, oldconfig)); } /* diff --git a/lib/libzfs_internal/Makefile.am b/lib/libzfs_internal/Makefile.am new file mode 100644 index 000000000000..a6add42a4cbc --- /dev/null +++ b/lib/libzfs_internal/Makefile.am @@ -0,0 +1,18 @@ +# +# libzfs_internal is library that contains functions that are common to both +# userspace code (like zpool_main.c) and to the libraries themselves, but +# are not exported as in the ABI. +# +include $(top_srcdir)/config/Rules.am + +DEFAULT_INCLUDES += -I$(srcdir) -I$(top_srcdir)/lib/libzfs + +lib_LTLIBRARIES = libzfs_internal.la + +dist_libzfs_internal_la_SOURCES = \ + libzfs_internal.c + +libzfs_internal_la_LIBADD = \ + $(abs_top_builddir)/lib/libnvpair/libnvpair.la + +include $(top_srcdir)/config/CppCheck.am diff --git a/lib/libzfs_internal/libzfs_internal.c b/lib/libzfs_internal/libzfs_internal.c new file mode 100644 index 000000000000..fe201a72073a --- /dev/null +++ b/lib/libzfs_internal/libzfs_internal.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +/* + * We want to call zpool_get_config() in this file, but that requires linking + * against libzfs... which requires this file as a dependency. We can't + * just move zpool_get_config() to this file, since zpool_get_config() is part + * of the ABI, and wouldn't get exposed here. + * + * To get around this, we add the zpool_get_config_impl() dummy function which + * we can call directly in this file, and then update zpool_get_config() to + * call zpool_get_config_impl(). + */ +nvlist_t * +zpool_get_config_impl(zpool_handle_t *zhp, nvlist_t **oldconfig) +{ + if (oldconfig) + *oldconfig = zhp->zpool_old_config; + return (zhp->zpool_config); +} + +static int +for_each_vdev_cb(zpool_handle_t *zhp, nvlist_t *nv, pool_vdev_iter_f func, + void *data) +{ + nvlist_t **child; + uint_t c, children; + int ret = 0; + int i; + char *type; + + const char *list[] = { + ZPOOL_CONFIG_SPARES, + ZPOOL_CONFIG_L2CACHE, + ZPOOL_CONFIG_CHILDREN + }; + + for (i = 0; i < ARRAY_SIZE(list); i++) { + if (nvlist_lookup_nvlist_array(nv, list[i], &child, + &children) == 0) { + for (c = 0; c < children; c++) { + uint64_t ishole = 0; + + (void) nvlist_lookup_uint64(child[c], + ZPOOL_CONFIG_IS_HOLE, &ishole); + + if (ishole) + continue; + + ret |= for_each_vdev_cb(zhp, child[c], func, + data); + } + } + } + + if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0) + return (ret); + + /* Don't run our function on root vdevs */ + if (strcmp(type, VDEV_TYPE_ROOT) != 0) { + ret |= func(zhp, nv, data); + } + + return (ret); +} + +/* + * This is the equivalent of for_each_pool() for vdevs. It iterates through + * all vdevs in the pool, ignoring root vdevs and holes, calling func() on + * each one. + * + * @zhp: Zpool handle + * @func: Function to call on each vdev + * @data: Custom data to pass to the function + */ +int +for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data) +{ + nvlist_t *config, *nvroot = NULL; + + if ((config = zpool_get_config_impl(zhp, NULL)) != NULL) { + verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvroot) == 0); + } + return (for_each_vdev_cb(zhp, nvroot, func, data)); +} + +/* + * Given an ZPOOL_CONFIG_VDEV_TREE nvpair, iterate over all the vdevs, calling + * func() for each one. func() is passed the vdev's nvlist and an optional + * user-defined 'data' pointer. + */ +int +for_each_vdev_in_nvlist(nvlist_t *nvroot, pool_vdev_iter_f func, void *data) +{ + return (for_each_vdev_cb(NULL, nvroot, func, data)); +} diff --git a/lib/libzutil/Makefile.am b/lib/libzutil/Makefile.am index 0bc29f05e0fb..d80ccc0ddc81 100644 --- a/lib/libzutil/Makefile.am +++ b/lib/libzutil/Makefile.am @@ -42,7 +42,8 @@ libzutil_la_LIBADD = \ $(abs_top_builddir)/lib/libavl/libavl.la \ $(abs_top_builddir)/lib/libtpool/libtpool.la \ $(abs_top_builddir)/lib/libnvpair/libnvpair.la \ - $(abs_top_builddir)/lib/libspl/libspl.la + $(abs_top_builddir)/lib/libspl/libspl.la \ + $(abs_top_builddir)/lib/libzfs_internal/libzfs_internal.la if BUILD_LINUX libzutil_la_LIBADD += \ diff --git a/lib/libzutil/os/freebsd/zutil_import_os.c b/lib/libzutil/os/freebsd/zutil_import_os.c index 2d8900ce2483..3da661f4c557 100644 --- a/lib/libzutil/os/freebsd/zutil_import_os.c +++ b/lib/libzutil/os/freebsd/zutil_import_os.c @@ -247,3 +247,8 @@ zfs_dev_flush(int fd __unused) { return (0); } + +void +update_vdevs_config_dev_sysfs_path(nvlist_t *config) +{ +} diff --git a/lib/libzutil/os/linux/zutil_import_os.c b/lib/libzutil/os/linux/zutil_import_os.c index 5defb526f210..efde276f7b38 100644 --- a/lib/libzutil/os/linux/zutil_import_os.c +++ b/lib/libzutil/os/linux/zutil_import_os.c @@ -65,6 +65,8 @@ #include #include #include +#include +#include #include "zutil_import.h" @@ -757,6 +759,58 @@ encode_device_strings(const char *path, vdev_dev_strs_t *ds, #endif } +/* + * Rescan the enclosure sysfs path for turning on enclosure LEDs and store it + * in the nvlist * (if applicable). Like: + * vdev_enc_sysfs_path: '/sys/class/enclosure/11:0:1:0/SLOT 4' + */ +static void +update_vdev_config_dev_sysfs_path(nvlist_t *nv, char *path) +{ + char *upath, *spath; + + /* Add enclosure sysfs path (if disk is in an enclosure). */ + upath = zfs_get_underlying_path(path); + spath = zfs_get_enclosure_sysfs_path(upath); + + if (spath) { + nvlist_add_string(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH, spath); + } else { + nvlist_remove_all(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH); + } + + free(upath); + free(spath); +} + +/* + * This will get called for each leaf vdev. + */ +static int +sysfs_path_pool_vdev_iter_f(zpool_handle_t *hdl, nvlist_t *nv, void *data) +{ + char *path = NULL; + if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0) + return (1); + + /* Rescan our enclosure sysfs path for this vdev */ + update_vdev_config_dev_sysfs_path(nv, path); + return (0); +} + +/* + * Given an nvlist for our pool (with vdev tree), iterate over all the + * leaf vdevs and update their ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH. + */ +void +update_vdevs_config_dev_sysfs_path(nvlist_t *config) +{ + nvlist_t *nvroot = NULL; + verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvroot) == 0); + for_each_vdev_in_nvlist(nvroot, sysfs_path_pool_vdev_iter_f, NULL); +} + /* * Update a leaf vdev's persistent device strings * @@ -783,7 +837,6 @@ update_vdev_config_dev_strs(nvlist_t *nv) vdev_dev_strs_t vds; char *env, *type, *path; uint64_t wholedisk = 0; - char *upath, *spath; /* * For the benefit of legacy ZFS implementations, allow @@ -830,18 +883,7 @@ update_vdev_config_dev_strs(nvlist_t *nv) (void) nvlist_add_string(nv, ZPOOL_CONFIG_PHYS_PATH, vds.vds_devphys); } - - /* Add enclosure sysfs path (if disk is in an enclosure). */ - upath = zfs_get_underlying_path(path); - spath = zfs_get_enclosure_sysfs_path(upath); - if (spath) - nvlist_add_string(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH, - spath); - else - nvlist_remove_all(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH); - - free(upath); - free(spath); + update_vdev_config_dev_sysfs_path(nv, path); } else { /* Clear out any stale entries. */ (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); diff --git a/lib/libzutil/zutil_import.c b/lib/libzutil/zutil_import.c index 95fd0ec0af85..9eb34cadd1a9 100644 --- a/lib/libzutil/zutil_import.c +++ b/lib/libzutil/zutil_import.c @@ -65,6 +65,7 @@ #include #include +#include #include #include "zutil_import.h" @@ -1704,6 +1705,8 @@ zpool_find_import_cached(libpc_handle_t *hdl, importargs_t *iarg) return (NULL); } + update_vdevs_config_dev_sysfs_path(src); + if ((dst = zutil_refresh_config(hdl, src)) == NULL) { nvlist_free(raw); nvlist_free(pools); diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index 2763bd8de1c4..0ba76f6b88d9 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -2373,6 +2373,7 @@ vdev_validate(vdev_t *vd) static void vdev_copy_path_impl(vdev_t *svd, vdev_t *dvd) { + char *old, *new; if (svd->vdev_path != NULL && dvd->vdev_path != NULL) { if (strcmp(svd->vdev_path, dvd->vdev_path) != 0) { zfs_dbgmsg("vdev_copy_path: vdev %llu: path changed " @@ -2386,6 +2387,29 @@ vdev_copy_path_impl(vdev_t *svd, vdev_t *dvd) zfs_dbgmsg("vdev_copy_path: vdev %llu: path set to '%s'", (u_longlong_t)dvd->vdev_guid, dvd->vdev_path); } + + /* + * Our enclosure sysfs path may have changed between imports + */ + old = dvd->vdev_enc_sysfs_path; + new = svd->vdev_enc_sysfs_path; + if ((old != NULL && new == NULL) || + (old == NULL && new != NULL) || + ((old != NULL && new != NULL) && strcmp(new, old) != 0)) { + zfs_dbgmsg("vdev_copy_path: vdev %llu: vdev_enc_sysfs_path " + "changed from '%s' to '%s'", (u_longlong_t)dvd->vdev_guid, + old, new); + + if (dvd->vdev_enc_sysfs_path) + spa_strfree(dvd->vdev_enc_sysfs_path); + + if (svd->vdev_enc_sysfs_path) { + dvd->vdev_enc_sysfs_path = spa_strdup( + svd->vdev_enc_sysfs_path); + } else { + dvd->vdev_enc_sysfs_path = NULL; + } + } } /*