diff options
author | Tony Hutter <[email protected]> | 2016-10-24 10:45:59 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2016-10-24 10:45:59 -0700 |
commit | 1bbd8770490f0e5b8c575865ab70f6853bca2a2a (patch) | |
tree | 302dc7e82db3c1b33739ec41998d95cfe0157450 /lib | |
parent | a85cefa35c00ab4999038fbed69a6c28d0244366 (diff) |
Turn on/off enclosure slot fault LED even when disk isn't present
Previously when a drive faulted, the statechange-led.sh script would lookup
the drive's LED sysfs entry in /sys/block/sd*/device/enclosure_device, and
turn it on. During testing we noticed that if you pulled out a drive, or if
the drive was so badly broken that it no longer appeared to Linux, that the
/sys/block/sd* path would be removed, and the script could not lookup the
LED entry.
To fix this, this patch looks up the disks's more persistent
"/sys/class/enclosure/X:X:X:X/Slot N" LED sysfs path at pool import. It then
passes that path to the statechange-led script to use, rather than having the
script look it up on the fly. This allows the script to turn on/off the slot
LEDs even when the drive is missing.
Closes #5309
Closes #2375
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libzfs/libzfs_import.c | 17 | ||||
-rw-r--r-- | lib/libzfs/libzfs_pool.c | 113 |
2 files changed, 126 insertions, 4 deletions
diff --git a/lib/libzfs/libzfs_import.c b/lib/libzfs/libzfs_import.c index e76f7432a..3de7fd73d 100644 --- a/lib/libzfs/libzfs_import.c +++ b/lib/libzfs/libzfs_import.c @@ -64,6 +64,7 @@ #include <blkid/blkid.h> #include "libzfs.h" #include "libzfs_impl.h" +#include <libzfs.h> /* * Intermediate structures used to gather configuration information. @@ -437,6 +438,10 @@ no_dev: * * multipath device node example: * devid: 'dm-uuid-mpath-35000c5006304de3f' + * + * We also store the enclosure sysfs path for turning on enclosure LEDs + * (if applicable): + * vdev_enc_sysfs_path: '/sys/class/enclosure/11:0:1:0/SLOT 4' */ void update_vdev_config_dev_strs(nvlist_t *nv) @@ -444,6 +449,7 @@ 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 @@ -470,6 +476,7 @@ update_vdev_config_dev_strs(nvlist_t *nv) !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2))) { (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); (void) nvlist_remove_all(nv, ZPOOL_CONFIG_PHYS_PATH); + (void) nvlist_remove_all(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH); return; } @@ -490,10 +497,20 @@ 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); + free(upath); + free(spath); } else { /* clear out any stale entries */ (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID); (void) nvlist_remove_all(nv, ZPOOL_CONFIG_PHYS_PATH); + (void) nvlist_remove_all(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH); } } #else diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index ebca76834..0641c1844 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -4407,7 +4407,7 @@ static char * dm_get_underlying_path(char *dm_name) goto end; if ((asprintf(&tmp, "/dev/block/%d:%d", child_info->major, - child_info->minor) == -1) || !tmp) + child_info->minor) == -1) || tmp == NULL) goto end; /* Further translate /dev/block/ name into the normal name */ @@ -4430,11 +4430,11 @@ end: * Return 0 if not. */ int -dev_is_dm(char *devname) +zfs_dev_is_dm(char *dev_name) { char *tmp; - tmp = dm_get_underlying_path(devname); + tmp = dm_get_underlying_path(dev_name); if (!tmp) return (0); @@ -4483,7 +4483,7 @@ dev_is_dm(char *devname) * NOTE: The returned name string must be *freed*. */ char * -get_underlying_path(libzfs_handle_t *hdl, char *dev_name) +zfs_get_underlying_path(char *dev_name) { char *name = NULL; char *tmp; @@ -4504,3 +4504,108 @@ get_underlying_path(libzfs_handle_t *hdl, char *dev_name) return (name); } + +/* + * Given a dev name like "sda", return the full enclosure sysfs path to + * the disk. You can also pass in the name with "/dev" prepended + * to it (like /dev/sda). + * + * For example, disk "sda" in enclosure slot 1: + * dev: "sda" + * returns: "/sys/class/enclosure/1:0:3:0/Slot 1" + * + * 'dev' must be a non-devicemapper device. + * + * Returned string must be freed. + */ +char * +zfs_get_enclosure_sysfs_path(char *dev_name) +{ + DIR *dp = NULL; + struct dirent *ep; + char buf[MAXPATHLEN]; + char *tmp1 = NULL; + char *tmp2 = NULL; + char *tmp3 = NULL; + char *path = NULL; + size_t size; + int tmpsize; + + if (!dev_name) + return (NULL); + + /* If they preface 'dev' with a path (like "/dev") then strip it off */ + tmp1 = strrchr(dev_name, '/'); + if (tmp1) + dev_name = tmp1 + 1; /* +1 since we want the chr after '/' */ + + tmpsize = asprintf(&tmp1, "/sys/block/%s/device", dev_name); + if (tmpsize == -1 || tmp1 == NULL) { + tmp1 = NULL; + goto end; + } + + dp = opendir(tmp1); + if (dp == NULL) { + tmp1 = NULL; /* To make free() at the end a NOP */ + goto end; + } + + /* + * Look though all sysfs entries in /sys/block/<dev>/device for + * the enclosure symlink. + */ + while ((ep = readdir(dp))) { + /* Ignore everything that's not our enclosure_device link */ + if (!strstr(ep->d_name, "enclosure_device")) + continue; + + if (asprintf(&tmp2, "%s/%s", tmp1, ep->d_name) == -1 || + tmp2 == NULL) + break; + + size = readlink(tmp2, buf, sizeof (buf)); + + /* Did readlink fail or crop the link name? */ + if (size == -1 || size >= sizeof (buf)) { + free(tmp2); + tmp2 = NULL; /* To make free() at the end a NOP */ + break; + } + + /* + * We got a valid link. readlink() doesn't terminate strings + * so we have to do it. + */ + buf[size] = '\0'; + + /* + * Our link will look like: + * + * "../../../../port-11:1:2/..STUFF../enclosure/1:0:3:0/SLOT 1" + * + * We want to grab the "enclosure/1:0:3:0/SLOT 1" part + */ + tmp3 = strstr(buf, "enclosure"); + if (tmp3 == NULL) + break; + + if (asprintf(&path, "/sys/class/%s", tmp3) == -1) { + /* If asprintf() fails, 'path' is undefined */ + path = NULL; + break; + } + + if (path == NULL) + break; + } + +end: + free(tmp2); + free(tmp1); + + if (dp) + closedir(dp); + + return (path); +} |