Rescan enclosure sysfs path on import

When you create a pool, zfs writes vd->vdev_enc_sysfs_path with the
enclosure sysfs path to the fault LEDs, like:

    vdev_enc_sysfs_path = /sys/class/enclosure/0:0:1:0/SLOT8

However, this enclosure path doesn't get updated on successive imports
even if enclosure path to the disk changes.  This patch fixes the issue.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Tony Hutter <hutter2@llnl.gov>
Closes #11950
Closes #12095
This commit is contained in:
Tony Hutter 2021-10-04 12:32:16 -07:00
parent 728cf93b0c
commit 91fe213823
9 changed files with 169 additions and 63 deletions

View File

@ -25,7 +25,8 @@ zpool_LDADD = \
$(abs_top_builddir)/lib/libzfs/libzfs.la \ $(abs_top_builddir)/lib/libzfs/libzfs.la \
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
$(abs_top_builddir)/lib/libnvpair/libnvpair.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
zpool_LDADD += $(LTLIBINTL) zpool_LDADD += $(LTLIBINTL)

View File

@ -264,51 +264,6 @@ for_each_pool(int argc, char **argv, boolean_t unavail,
return (ret); 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 * 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 * all vdevs in the pool, ignoring root vdevs and holes, calling func() on
@ -327,7 +282,7 @@ for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data)
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0); &nvroot) == 0);
} }
return (for_each_vdev_cb(zhp, nvroot, func, data)); return (for_each_vdev_cb((void *) zhp, nvroot, func, data));
} }
/* /*
@ -598,7 +553,7 @@ vdev_run_cmd_thread(void *cb_cmd_data)
/* For each vdev in the pool run a command */ /* For each vdev in the pool run a command */
static int static int
for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl) for_each_vdev_run_cb(void *zhp_data, nvlist_t *nv, void *cb_vcdl)
{ {
vdev_cmd_data_list_t *vcdl = cb_vcdl; vdev_cmd_data_list_t *vcdl = cb_vcdl;
vdev_cmd_data_t *data; vdev_cmd_data_t *data;
@ -606,6 +561,7 @@ for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl)
char *vname = NULL; char *vname = NULL;
char *vdev_enc_sysfs_path = NULL; char *vdev_enc_sysfs_path = NULL;
int i, match = 0; int i, match = 0;
zpool_handle_t *zhp = zhp_data;
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0) if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
return (1); return (1);

View File

@ -5049,11 +5049,12 @@ get_stat_flags(zpool_list_t *list)
* Return 1 if cb_data->cb_vdev_names[0] is this vdev's name, 0 otherwise. * Return 1 if cb_data->cb_vdev_names[0] is this vdev's name, 0 otherwise.
*/ */
static int static int
is_vdev_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_data) is_vdev_cb(void *zhp_data, nvlist_t *nv, void *cb_data)
{ {
iostat_cbdata_t *cb = cb_data; iostat_cbdata_t *cb = cb_data;
char *name = NULL; char *name = NULL;
int ret = 0; int ret = 0;
zpool_handle_t *zhp = zhp_data;
name = zpool_vdev_name(g_zfs, zhp, nv, cb->cb_name_flags); name = zpool_vdev_name(g_zfs, zhp, nv, cb->cb_name_flags);

View File

@ -27,6 +27,7 @@
#include <libnvpair.h> #include <libnvpair.h>
#include <libzfs.h> #include <libzfs.h>
#include <libzutil.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -67,7 +68,6 @@ int for_each_pool(int, char **, boolean_t unavail, zprop_list_t **,
boolean_t, zpool_iter_f, void *); boolean_t, zpool_iter_f, void *);
/* Vdev list functions */ /* 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); int for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data);
typedef struct zpool_list zpool_list_t; typedef struct zpool_list zpool_list_t;

View File

@ -159,6 +159,16 @@ void color_start(char *color);
void color_end(void); void color_end(void);
int printf_color(char *color, char *format, ...); int printf_color(char *color, char *format, ...);
/*
* These functions are used by the ZFS libraries and cmd/zpool code, but are
* not exported in the ABI.
*/
typedef int (*pool_vdev_iter_f)(void *, nvlist_t *, void *);
int for_each_vdev_cb(void *zhp, nvlist_t *nv, pool_vdev_iter_f func,
void *data);
int for_each_vdev_in_nvlist(nvlist_t *nvroot, pool_vdev_iter_f func,
void *data);
void update_vdevs_config_dev_sysfs_path(nvlist_t *config);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -237,3 +237,8 @@ zfs_dev_flush(int fd __unused)
{ {
return (0); return (0);
} }
void
update_vdevs_config_dev_sysfs_path(nvlist_t *config)
{
}

View File

@ -66,6 +66,7 @@
#include <thread_pool.h> #include <thread_pool.h>
#include <libzutil.h> #include <libzutil.h>
#include <libnvpair.h> #include <libnvpair.h>
#include <libzfs.h>
#include "zutil_import.h" #include "zutil_import.h"
@ -777,6 +778,58 @@ no_dev:
#endif #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(void *hdl_data, 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 * Update a leaf vdev's persistent device strings
* *
@ -803,7 +856,6 @@ update_vdev_config_dev_strs(nvlist_t *nv)
vdev_dev_strs_t vds; vdev_dev_strs_t vds;
char *env, *type, *path; char *env, *type, *path;
uint64_t wholedisk = 0; uint64_t wholedisk = 0;
char *upath, *spath;
/* /*
* For the benefit of legacy ZFS implementations, allow * For the benefit of legacy ZFS implementations, allow
@ -850,18 +902,7 @@ update_vdev_config_dev_strs(nvlist_t *nv)
(void) nvlist_add_string(nv, ZPOOL_CONFIG_PHYS_PATH, (void) nvlist_add_string(nv, ZPOOL_CONFIG_PHYS_PATH,
vds.vds_devphys); vds.vds_devphys);
} }
update_vdev_config_dev_sysfs_path(nv, path);
/* 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);
} else { } else {
/* Clear out any stale entries. */ /* Clear out any stale entries. */
(void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);

View File

@ -1534,6 +1534,8 @@ zpool_find_import_cached(libpc_handle_t *hdl, importargs_t *iarg)
return (NULL); return (NULL);
} }
update_vdevs_config_dev_sysfs_path(src);
if ((dst = zutil_refresh_config(hdl, src)) == NULL) { if ((dst = zutil_refresh_config(hdl, src)) == NULL) {
nvlist_free(raw); nvlist_free(raw);
nvlist_free(pools); nvlist_free(pools);
@ -1691,3 +1693,69 @@ zpool_find_config(void *hdl, const char *target, nvlist_t **configp,
return (0); return (0);
} }
/*
* Internal function for iterating over the vdevs.
*
* For each vdev, func() will be called and will be passed 'zhp' (which is
* typically the zpool_handle_t cast as a void pointer), the vdev's nvlist, and
* a user-defined data pointer).
*
* The return values from all the func() calls will be OR'd together and
* returned.
*/
int
for_each_vdev_cb(void *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);
}
/*
* 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));
}

View File

@ -2164,6 +2164,7 @@ vdev_validate(vdev_t *vd)
static void static void
vdev_copy_path_impl(vdev_t *svd, vdev_t *dvd) vdev_copy_path_impl(vdev_t *svd, vdev_t *dvd)
{ {
char *old, *new;
if (svd->vdev_path != NULL && dvd->vdev_path != NULL) { if (svd->vdev_path != NULL && dvd->vdev_path != NULL) {
if (strcmp(svd->vdev_path, dvd->vdev_path) != 0) { if (strcmp(svd->vdev_path, dvd->vdev_path) != 0) {
zfs_dbgmsg("vdev_copy_path: vdev %llu: path changed " zfs_dbgmsg("vdev_copy_path: vdev %llu: path changed "
@ -2177,6 +2178,29 @@ vdev_copy_path_impl(vdev_t *svd, vdev_t *dvd)
zfs_dbgmsg("vdev_copy_path: vdev %llu: path set to '%s'", zfs_dbgmsg("vdev_copy_path: vdev %llu: path set to '%s'",
(u_longlong_t)dvd->vdev_guid, dvd->vdev_path); (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;
}
}
} }
/* /*