mirror of
https://github.com/openzfs/zfs.git
synced 2025-10-01 02:46:29 +00:00

Historically, ZED has blindly spawned off zedlets in parallel and never worried about their completion order. This means that you can potentially have zedlets for event number 2 starting before zedlets for event number 1 had finished. Most of the time this is fine, and it actually helps a lot when the system is getting spammed with hundreds of events. However, there are times when you want your zedlets to be executed in sequence with the event ID. That is where synchronous zedlets come in. ZED will wait for all previously spawned zedlets to finish before running a synchronous zedlet. Synchronous zedlets are guaranteed to be the only zedlet running. No other zedlets may run in parallel with a synchronous zedlet. Users should be careful to only use synchronous zedlets when needed, since they decrease parallelism. To make a zedlet synchronous, simply add a "-sync-" immediately following the event name in the zedlet's file name: EVENT_NAME-sync-ZEDLETNAME.sh For example, if you wanted a synchronous statechange script: statechange-sync-myzedlet.sh Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Tony Hutter <hutter2@llnl.gov> Closes #17335
243 lines
5.9 KiB
Bash
Executable File
243 lines
5.9 KiB
Bash
Executable File
#!/bin/sh
|
|
# shellcheck disable=SC2154
|
|
#
|
|
# Turn off/on vdevs' enclosure fault LEDs when their pool's state changes.
|
|
#
|
|
# Turn a vdev's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL.
|
|
# Turn its LED off when it's back ONLINE again.
|
|
#
|
|
# This script run in two basic modes:
|
|
#
|
|
# 1. If $ZEVENT_VDEV_ENC_SYSFS_PATH and $ZEVENT_VDEV_STATE_STR are set, then
|
|
# only set the LED for that particular vdev. This is the case for statechange
|
|
# events and some vdev_* events.
|
|
#
|
|
# 2. If those vars are not set, then check the state of all vdevs in the pool
|
|
# and set the LEDs accordingly. This is the case for pool_import events.
|
|
#
|
|
# Note that this script requires that your enclosure be supported by the
|
|
# Linux SCSI Enclosure services (SES) driver. The script will do nothing
|
|
# if you have no enclosure, or if your enclosure isn't supported.
|
|
#
|
|
# Exit codes:
|
|
# 0: enclosure led successfully set
|
|
# 1: enclosure leds not available
|
|
# 2: enclosure leds administratively disabled
|
|
# 3: The led sysfs path passed from ZFS does not exist
|
|
# 4: $ZPOOL not set
|
|
# 5: awk is not installed
|
|
|
|
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
|
|
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
|
|
|
|
if [ ! -d /sys/class/enclosure ] && [ ! -d /sys/bus/pci/slots ] ; then
|
|
# No JBOD enclosure or NVMe slots
|
|
exit 1
|
|
fi
|
|
|
|
if [ "${ZED_USE_ENCLOSURE_LEDS}" != "1" ] ; then
|
|
exit 2
|
|
fi
|
|
|
|
zed_check_cmd "$ZPOOL" || exit 4
|
|
zed_check_cmd awk || exit 5
|
|
|
|
# Global used in set_led debug print
|
|
vdev=""
|
|
|
|
# check_and_set_led (file, val)
|
|
#
|
|
# Read an enclosure sysfs file, and write it if it's not already set to 'val'
|
|
#
|
|
# Arguments
|
|
# file: sysfs file to set (like /sys/class/enclosure/0:0:1:0/SLOT 10/fault)
|
|
# val: value to set it to
|
|
#
|
|
# Return
|
|
# 0 on success, 3 on missing sysfs path
|
|
#
|
|
check_and_set_led()
|
|
{
|
|
file="$1"
|
|
val="$2"
|
|
|
|
if [ -z "$val" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if [ ! -e "$file" ] ; then
|
|
return 3
|
|
fi
|
|
|
|
# If another process is accessing the LED when we attempt to update it,
|
|
# the update will be lost so retry until the LED actually changes or we
|
|
# timeout.
|
|
for _ in 1 2 3 4 5; do
|
|
# We want to check the current state first, since writing to the
|
|
# 'fault' entry always causes a SES command, even if the
|
|
# current state is already what you want.
|
|
read -r current < "${file}"
|
|
|
|
# On some enclosures if you write 1 to fault, and read it back,
|
|
# it will return 2. Treat all non-zero values as 1 for
|
|
# simplicity.
|
|
if [ "$current" != "0" ] ; then
|
|
current=1
|
|
fi
|
|
|
|
if [ "$current" != "$val" ] ; then
|
|
echo "$val" > "$file"
|
|
zed_log_msg "vdev $vdev set '$file' LED to $val"
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Fault LEDs for JBODs and NVMe drives are handled a little differently.
|
|
#
|
|
# On JBODs the fault LED is called 'fault' and on a path like this:
|
|
#
|
|
# /sys/class/enclosure/0:0:1:0/SLOT 10/fault
|
|
#
|
|
# On NVMe it's called 'attention' and on a path like this:
|
|
#
|
|
# /sys/bus/pci/slot/0/attention
|
|
#
|
|
# This function returns the full path to the fault LED file for a given
|
|
# enclosure/slot directory.
|
|
#
|
|
path_to_led()
|
|
{
|
|
dir=$1
|
|
if [ -f "$dir/fault" ] ; then
|
|
echo "$dir/fault"
|
|
elif [ -f "$dir/attention" ] ; then
|
|
echo "$dir/attention"
|
|
fi
|
|
}
|
|
|
|
state_to_val()
|
|
{
|
|
state="$1"
|
|
case "$state" in
|
|
FAULTED|DEGRADED|UNAVAIL|REMOVED)
|
|
echo 1
|
|
;;
|
|
ONLINE)
|
|
echo 0
|
|
;;
|
|
*)
|
|
echo "invalid state: $state"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
#
|
|
# Given a nvme name like 'nvme0n1', pass back its slot directory
|
|
# like "/sys/bus/pci/slots/0"
|
|
#
|
|
nvme_dev_to_slot()
|
|
{
|
|
dev="$1"
|
|
|
|
# Get the address "0000:01:00.0"
|
|
read -r address < "/sys/class/block/$dev/device/address"
|
|
|
|
find /sys/bus/pci/slots -regex '.*/[0-9]+/address$' | \
|
|
while read -r sys_addr; do
|
|
read -r this_address < "$sys_addr"
|
|
|
|
# The format of address is a little different between
|
|
# /sys/class/block/$dev/device/address and
|
|
# /sys/bus/pci/slots/
|
|
#
|
|
# address= "0000:01:00.0"
|
|
# this_address = "0000:01:00"
|
|
#
|
|
if echo "$address" | grep -Eq ^"$this_address" ; then
|
|
echo "${sys_addr%/*}"
|
|
break
|
|
fi
|
|
done
|
|
}
|
|
|
|
|
|
# process_pool (pool)
|
|
#
|
|
# Iterate through a pool and set the vdevs' enclosure slot LEDs to
|
|
# those vdevs' state.
|
|
#
|
|
# Arguments
|
|
# pool: Pool name.
|
|
#
|
|
# Return
|
|
# 0 on success, 3 on missing sysfs path
|
|
#
|
|
process_pool()
|
|
{
|
|
pool="$1"
|
|
|
|
# The output will be the vdevs only (from "grep '/dev/'"):
|
|
#
|
|
# U45 ONLINE 0 0 0 /dev/sdk 0
|
|
# U46 ONLINE 0 0 0 /dev/sdm 0
|
|
# U47 ONLINE 0 0 0 /dev/sdn 0
|
|
# U50 ONLINE 0 0 0 /dev/sdbn 0
|
|
#
|
|
ZPOOL_SCRIPTS_AS_ROOT=1 $ZPOOL status -c upath,fault_led "$pool" | grep '/dev/' | (
|
|
rc=0
|
|
while read -r vdev state _ _ _ therest; do
|
|
# Read out current LED value and path
|
|
# Get dev name (like 'sda')
|
|
dev=$(basename "$(echo "$therest" | awk '{print $(NF-1)}')")
|
|
vdev_enc_sysfs_path=$(realpath "/sys/class/block/$dev/device/enclosure_device"*)
|
|
if [ ! -d "$vdev_enc_sysfs_path" ] ; then
|
|
# This is not a JBOD disk, but it could be a PCI NVMe drive
|
|
vdev_enc_sysfs_path=$(nvme_dev_to_slot "$dev")
|
|
fi
|
|
|
|
current_val=$(echo "$therest" | awk '{print $NF}')
|
|
|
|
if [ "$current_val" != "0" ] ; then
|
|
current_val=1
|
|
fi
|
|
|
|
if [ -z "$vdev_enc_sysfs_path" ] ; then
|
|
# Skip anything with no sysfs LED entries
|
|
continue
|
|
fi
|
|
|
|
led_path=$(path_to_led "$vdev_enc_sysfs_path")
|
|
if [ ! -e "$led_path" ] ; then
|
|
rc=3
|
|
zed_log_msg "vdev $vdev '$led_path' doesn't exist"
|
|
continue
|
|
fi
|
|
|
|
val=$(state_to_val "$state")
|
|
|
|
if [ "$current_val" = "$val" ] ; then
|
|
# LED is already set correctly
|
|
continue
|
|
fi
|
|
|
|
if ! check_and_set_led "$led_path" "$val"; then
|
|
rc=3
|
|
fi
|
|
done
|
|
exit "$rc"; )
|
|
}
|
|
|
|
if [ -n "$ZEVENT_VDEV_ENC_SYSFS_PATH" ] && [ -n "$ZEVENT_VDEV_STATE_STR" ] ; then
|
|
# Got a statechange for an individual vdev
|
|
val=$(state_to_val "$ZEVENT_VDEV_STATE_STR")
|
|
vdev=$(basename "$ZEVENT_VDEV_PATH")
|
|
ledpath=$(path_to_led "$ZEVENT_VDEV_ENC_SYSFS_PATH")
|
|
check_and_set_led "$ledpath" "$val"
|
|
else
|
|
# Process the entire pool
|
|
poolname=$(zed_guid_to_pool "$ZEVENT_POOL_GUID")
|
|
process_pool "$poolname"
|
|
fi
|