Skip to content

Commit b291029

Browse files
tonyhutterbehlendorf
authored andcommitted
Enclosure LED fixes
- Pass $VDEV_ENC_SYSFS_PATH to 'zpool [iostat|status] -c' to include enclosure LED sysfs path. - Set LEDs correctly after import. This includes clearing any erroniously set LEDs prior to the import, and setting the LED for any UNAVAIL drives. - Include symlink for vdev_attach-led.sh in Makefile.am. - Print the VDEV path in all-syslog.sh, and fix it so the pool GUID actually prints. Reviewed-by: Don Brady <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Tony Hutter <[email protected]> Closes #5716 Closes #5751
1 parent 65a736b commit b291029

File tree

8 files changed

+204
-51
lines changed

8 files changed

+204
-51
lines changed

cmd/zed/Makefile.am

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ dist_zedexec_SCRIPTS = \
6767
zed.d/scrub_finish-notify.sh \
6868
zed.d/statechange-led.sh \
6969
zed.d/statechange-notify.sh \
70-
zed.d/vdev_clear-led.sh
70+
zed.d/vdev_clear-led.sh \
71+
zed.d/vdev_attach-led.sh \
72+
zed.d/pool_import-led.sh
7173

7274
zedconfdefaults = \
7375
all-syslog.sh \
@@ -76,7 +78,9 @@ zedconfdefaults = \
7678
scrub_finish-notify.sh \
7779
statechange-led.sh \
7880
statechange-notify.sh \
79-
vdev_clear-led.sh
81+
vdev_clear-led.sh \
82+
vdev_attach-led.sh \
83+
pool_import-led.sh
8084

8185
install-data-hook:
8286
$(MKDIR_P) "$(DESTDIR)$(zedconfdir)"

cmd/zed/zed.d/all-syslog.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
77

88
zed_log_msg "eid=${ZEVENT_EID}" "class=${ZEVENT_SUBCLASS}" \
9-
"${ZEVENT_POOL:+"pool=${ZEVENT_POOL}"}" \
9+
"${ZEVENT_POOL_GUID:+"pool_guid=${ZEVENT_POOL_GUID}"}" \
10+
"${ZEVENT_VDEV_PATH:+"vdev_path=${ZEVENT_VDEV_PATH}"}" \
1011
"${ZEVENT_VDEV_STATE_STR:+"vdev_state=${ZEVENT_VDEV_STATE_STR}"}"
1112
exit 0

cmd/zed/zed.d/pool_import-led.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
statechange-led.sh

cmd/zed/zed.d/statechange-led.sh

Lines changed: 138 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,28 @@
22
#
33
# Turn off/on the VDEV's enclosure fault LEDs when the pool's state changes.
44
#
5-
# Turn LED on if the VDEV becomes faulted or degraded, and turn it back off
6-
# when it's online again. It will also turn on the LED (or keep it on) if
7-
# the drive becomes unavailable, unless the drive was in was a previously
8-
# online state (online->unavail is a normal state transition during an
9-
# autoreplace).
5+
# Turn the VDEV's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL.
6+
# Turn the LED off when it's back ONLINE again.
107
#
11-
# This script requires that your enclosure be supported by the
8+
# This script run in two basic modes:
9+
#
10+
# 1. If $ZEVENT_VDEV_ENC_SYSFS_PATH and $ZEVENT_VDEV_STATE_STR are set, then
11+
# only set the LED for that particular VDEV. This is the case for statechange
12+
# events and some vdev_* events.
13+
#
14+
# 2. If those vars are not set, then check the state of all VDEVs in the pool,i
15+
# and set the LEDs accordingly. This is the case for pool_import events.
16+
#
17+
# Note that this script requires that your enclosure be supported by the
1218
# Linux SCSI enclosure services (ses) driver. The script will do nothing
1319
# if you have no enclosure, or if your enclosure isn't supported.
1420
#
1521
# Exit codes:
1622
# 0: enclosure led successfully set
1723
# 1: enclosure leds not not available
1824
# 2: enclosure leds administratively disabled
19-
# 3: ZED didn't pass enclosure sysfs path
20-
# 4: Enclosure sysfs path doesn't exist
25+
# 3: The led sysfs path passed from ZFS does not exist
26+
# 4: $ZPOOL not set
2127

2228
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
2329
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
@@ -30,15 +36,54 @@ if [ "${ZED_USE_ENCLOSURE_LEDS}" != "1" ] ; then
3036
exit 2
3137
fi
3238

33-
[ -n "${ZEVENT_VDEV_ENC_SYSFS_PATH}" ] || exit 3
39+
zed_check_cmd "$ZPOOL" || exit 4
40+
41+
# Global used in set_led debug print
42+
vdev=""
43+
44+
# set_led (file)
45+
#
46+
# Write an enclosure sysfs file
47+
#
48+
# Arguments
49+
# file: sysfs file to set (like /sys/class/enclosure/0:0:1:0/SLOT 10/fault)
50+
# val: value to set it to
51+
#
52+
# Globals
53+
# vdev: VDEV name for debug printing
54+
#
55+
# Return
56+
# nothing
57+
#
58+
function set_led {
59+
file="$1"
60+
val="$2"
3461

35-
[ -e "${ZEVENT_VDEV_ENC_SYSFS_PATH}/fault" ] || exit 4
62+
# Set the value twice. I've seen enclosures that were
63+
# flakey about setting it the first time.
64+
echo "$val" > "$file"
65+
echo "$val" > "$file"
66+
zed_log_msg "vdev $vdev set '$file' LED to $val"
67+
}
3668

37-
# Turn on/off enclosure LEDs
38-
function led
69+
# check_and_set_led (file, val)
70+
#
71+
# Read an enclosure sysfs file, and write it if it's not already set to 'val'
72+
#
73+
# Arguments
74+
# file: sysfs file to set (like /sys/class/enclosure/0:0:1:0/SLOT 10/fault)
75+
# val: value to set it to
76+
#
77+
# Return
78+
# 0 on success, 3 on missing sysfs path
79+
#
80+
function check_and_set_led
3981
{
40-
file="$1/fault"
41-
val=$2
82+
file="$1"
83+
val="$2"
84+
if [ ! -e "$ZEVENT_VDEV_ENC_SYSFS_PATH/fault" ] ; then
85+
return 3
86+
fi
4287

4388
# We want to check the current state first, since writing to the
4489
# 'fault' entry always always causes a SES command, even if the
@@ -53,28 +98,88 @@ function led
5398
fi
5499

55100
if [ "$current" != "$val" ] ; then
56-
# Set the value twice. I've seen enclosures that were
57-
# flakey about setting it the first time.
58-
echo "$val" > "$file"
59-
echo "$val" > "$file"
101+
set_led "$file" "$val"
60102
fi
61103
}
62104

63-
# Decide whether to turn on/off an LED based on the state
64-
# Pass in path name and fault string ("ONLINE"/"FAULTED"/"DEGRADED"...etc)
65-
#
66-
# We only turn on LEDs when a drive becomes FAULTED, DEGRADED, or UNAVAIL and
67-
# only turn it on when it comes back ONLINE. All other states are ignored, and
68-
# keep the previous LED state.
69-
function process {
70-
path="$1"
71-
fault=$2
72-
if [ "$fault" == "FAULTED" ] || [ "$fault" == "DEGRADED" ] || \
73-
[ "$fault" == "UNAVAIL" ] ; then
74-
led "$path" 1
75-
elif [ "$fault" == "ONLINE" ] ; then
76-
led "$path" 0
105+
function state_to_val {
106+
state="$1"
107+
if [ "$state" == "FAULTED" ] || [ "$state" == "DEGRADED" ] || \
108+
[ "$state" == "UNAVAIL" ] ; then
109+
echo 1
110+
elif [ "$state" == "ONLINE" ] ; then
111+
echo 0
77112
fi
78113
}
79114

80-
process "$ZEVENT_VDEV_ENC_SYSFS_PATH" "$ZEVENT_VDEV_STATE_STR"
115+
# process_pool ([pool])
116+
#
117+
# Iterate through a pool (or pools) and set the VDEV's enclosure slot LEDs to
118+
# the VDEV's state.
119+
#
120+
# Arguments
121+
# pool: Optional pool name. If not specified, iterate though all pools.
122+
#
123+
# Return
124+
# 0 on success, 3 on missing sysfs path
125+
#
126+
function process_pool
127+
{
128+
pool="$1"
129+
rc=0
130+
131+
# Lookup all the current LED values and paths in parallel
132+
cmd='echo led_token=$(cat "$VDEV_ENC_SYSFS_PATH/fault"),"$VDEV_ENC_SYSFS_PATH",'
133+
out=$($ZPOOL status -vc "$cmd" $pool | grep 'led_token=')
134+
while read -r vdev state read write chksum therest ; do
135+
136+
# Read out current LED value and path
137+
tmp=$(echo "$therest" | sed 's/^.*led_token=//g')
138+
IFS="," read -r current_val vdev_enc_sysfs_path <<< "$tmp"
139+
140+
if [ -z "$vdev_enc_sysfs_path" ] ; then
141+
# Skip anything with no sysfs LED entries
142+
continue
143+
fi
144+
145+
# On some enclosures if you write 1 to fault, and read it back,
146+
# it will return 2. Treat all non-zero values as 1 for
147+
# simplicity.
148+
if [ "$current_val" != "0" ] ; then
149+
current_val=1
150+
fi
151+
152+
val=$(state_to_val "$state")
153+
154+
if [ "$current_val" == "$val" ] ; then
155+
# LED is already set correctly
156+
continue
157+
fi
158+
159+
if [ ! -e "$vdev_enc_sysfs_path/fault" ] ; then
160+
rc=1
161+
zed_log_msg "vdev $vdev '$file/fault' doesn't exist"
162+
continue;
163+
fi
164+
165+
set_led "$vdev_enc_sysfs_path/fault" $val
166+
167+
done <<< "$out"
168+
if [ "$rc" == "0" ] ; then
169+
return 0
170+
else
171+
# We didn't see a sysfs entry that we wanted to set
172+
return 3
173+
fi
174+
}
175+
176+
if [ ! -z "$ZEVENT_VDEV_ENC_SYSFS_PATH" ] && [ ! -z "$ZEVENT_VDEV_STATE_STR" ] ; then
177+
# Got a statechange for an individual VDEV
178+
val=$(state_to_val "$ZEVENT_VDEV_STATE_STR")
179+
180+
vdev="$(basename $ZEVENT_VDEV_PATH)"
181+
check_and_set_led "$ZEVENT_VDEV_ENC_SYSFS_PATH/fault" "$val"
182+
else
183+
# Process the entire pool
184+
process_pool $(zed_guid_to_pool $ZEVENT_POOL_GUID)
185+
fi

cmd/zed/zed.d/zed-functions.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,25 @@ zed_rate_limit()
413413
zed_unlock "${lockfile}" "${lockfile_fd}"
414414
return "${rv}"
415415
}
416+
417+
418+
# zed_guid_to_pool (guid)
419+
#
420+
# Convert a pool GUID into its pool name (like "tank")
421+
# Arguments
422+
# guid: pool GUID (decimal or hex)
423+
#
424+
# Return
425+
# Pool name
426+
#
427+
function zed_guid_to_pool
428+
{
429+
if [ -z "$1" ] ; then
430+
return
431+
fi
432+
433+
guid=$(printf "%llu" $1)
434+
if [ ! -z "$guid" ] ; then
435+
$ZPOOL get -H -ovalue,name guid | awk '$1=='$guid' {print $2}'
436+
fi
437+
}

cmd/zpool/zpool_iter.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,11 @@ vdev_run_cmd_thread(void *cb_cmd_data)
332332
char cmd[_POSIX_ARG_MAX];
333333

334334
/* Set our VDEV_PATH and VDEV_UPATH env vars and run command */
335-
if (snprintf(cmd, sizeof (cmd), "VDEV_PATH=%s && VDEV_UPATH=%s && %s",
336-
data->path, data->upath ? data->upath : "\"\"", data->cmd) >=
337-
sizeof (cmd)) {
335+
if (snprintf(cmd, sizeof (cmd), "VDEV_PATH=%s && VDEV_UPATH=\"%s\" && "
336+
"VDEV_ENC_SYSFS_PATH=\"%s\" && %s", data->path ? data->path : "",
337+
data->upath ? data->upath : "",
338+
data->vdev_enc_sysfs_path ? data->vdev_enc_sysfs_path : "",
339+
data->cmd) >= sizeof (cmd)) {
338340
/* Our string was truncated */
339341
return;
340342
}
@@ -364,11 +366,15 @@ for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl)
364366
vdev_cmd_data_t *data;
365367
char *path = NULL;
366368
char *vname = NULL;
369+
char *vdev_enc_sysfs_path = NULL;
367370
int i, match = 0;
368371

369372
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
370373
return (1);
371374

375+
nvlist_lookup_string(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
376+
&vdev_enc_sysfs_path);
377+
372378
/* Spares show more than once if they're in use, so skip if exists */
373379
for (i = 0; i < vcdl->count; i++) {
374380
if ((strcmp(vcdl->data[i].path, path) == 0) &&
@@ -406,6 +412,10 @@ for_each_vdev_run_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_vcdl)
406412
data->path = strdup(path);
407413
data->upath = zfs_get_underlying_path(path);
408414
data->cmd = vcdl->cmd;
415+
if (vdev_enc_sysfs_path)
416+
data->vdev_enc_sysfs_path = strdup(vdev_enc_sysfs_path);
417+
else
418+
data->vdev_enc_sysfs_path = NULL;
409419

410420
vcdl->count++;
411421

@@ -500,6 +510,7 @@ free_vdev_cmd_data_list(vdev_cmd_data_list_t *vcdl)
500510
free(vcdl->data[i].pool);
501511
free(vcdl->data[i].upath);
502512
free(vcdl->data[i].line);
513+
free(vcdl->data[i].vdev_enc_sysfs_path);
503514
}
504515
free(vcdl->data);
505516
free(vcdl);

cmd/zpool/zpool_util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ typedef struct vdev_cmd_data
8080
char *upath; /* vdev underlying path */
8181
char *pool; /* Pool name */
8282
char *cmd; /* backpointer to cmd */
83+
char *vdev_enc_sysfs_path; /* enclosure sysfs path (if any) */
8384
} vdev_cmd_data_t;
8485

8586
typedef struct vdev_cmd_data_list

man/man8/zpool.8

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,12 +1548,16 @@ base 1024. To get the raw values, use the \fB-p\fR flag.
15481548
Run a command on each vdev and include first line of output
15491549
.sp
15501550
The \fB-c\fR option allows you to run an arbitrary command on each vdev and
1551-
display the first line of output in zpool iostat. The environment vars
1552-
\fBVDEV_PATH\fR and \fBVDEV_UPATH\fR are set to the vdev's path and "underlying
1553-
path" before running the command. For device mapper, multipath, or partitioned
1554-
vdevs, \fBVDEV_UPATH\fR is the actual underlying /dev/sd* disk. This can be
1555-
useful if the command you're running requires a /dev/sd* device. Commands run
1556-
in parallel for each vdev for performance.
1551+
display the first line of output in zpool iostat. The following environment
1552+
vars are set before running each command:
1553+
.sp
1554+
\fB$VDEV_PATH\fR: Full path to the vdev.
1555+
.LP
1556+
\fB$VDEV_UPATH\fR: "Underlying path" to the vdev. For device mapper, multipath, or
1557+
partitioned vdevs, \fBVDEV_UPATH\fR is the actual underlying /dev/sd* disk.
1558+
This can be useful if the command you're running requires a /dev/sd* device.
1559+
.LP
1560+
\fB$VDEV_ENC_SYSFS_PATH\fR: The sysfs path to the vdev's enclosure LEDs (if any).
15571561
.RE
15581562

15591563
.sp
@@ -2116,12 +2120,16 @@ If a scrub or resilver is in progress, this command reports the percentage done
21162120
Run a command on each vdev and include first line of output
21172121
.sp
21182122
The \fB-c\fR option allows you to run an arbitrary command on each vdev and
2119-
display the first line of output in zpool status. The environment vars
2120-
\fBVDEV_PATH\fR and \fBVDEV_UPATH\fR are set to the vdev's path and "underlying
2121-
path" before running the command. For device mapper, multipath, or partitioned
2122-
vdevs, \fBVDEV_UPATH\fR is the actual underlying /dev/sd* disk. This can be
2123-
useful if the command you're running requires a /dev/sd* device. Commands run
2124-
in parallel for each vdev for performance.
2123+
display the first line of output in zpool iostat. The following environment
2124+
vars are set before running each command:
2125+
.sp
2126+
\fB$VDEV_PATH\fR: Full path to the vdev.
2127+
.LP
2128+
\fB$VDEV_UPATH\fR: "Underlying path" to the vdev. For device mapper, multipath, or
2129+
partitioned vdevs, \fBVDEV_UPATH\fR is the actual underlying /dev/sd* disk.
2130+
This can be useful if the command you're running requires a /dev/sd* device.
2131+
.LP
2132+
\fB$VDEV_ENC_SYSFS_PATH\fR: The sysfs path to the vdev's enclosure LEDs (if any).
21252133
.RE
21262134

21272135
.sp

0 commit comments

Comments
 (0)