mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
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
This commit is contained in:
committed by
Brian Behlendorf
parent
a85cefa35c
commit
1bbd877049
@@ -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
|
||||
|
||||
+109
-4
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user