1eda14cbcSMatt Macy#!/bin/sh 2e92ffd9bSMartin Matuska# shellcheck disable=SC2154 3eda14cbcSMatt Macy# 416038816SMartin Matuska# Turn off/on vdevs' enclosure fault LEDs when their pool's state changes. 5eda14cbcSMatt Macy# 616038816SMartin Matuska# Turn a vdev's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL. 716038816SMartin Matuska# Turn its LED off when it's back ONLINE again. 8eda14cbcSMatt Macy# 9eda14cbcSMatt Macy# This script run in two basic modes: 10eda14cbcSMatt Macy# 11eda14cbcSMatt Macy# 1. If $ZEVENT_VDEV_ENC_SYSFS_PATH and $ZEVENT_VDEV_STATE_STR are set, then 1216038816SMartin Matuska# only set the LED for that particular vdev. This is the case for statechange 13eda14cbcSMatt Macy# events and some vdev_* events. 14eda14cbcSMatt Macy# 1516038816SMartin Matuska# 2. If those vars are not set, then check the state of all vdevs in the pool 16eda14cbcSMatt Macy# and set the LEDs accordingly. This is the case for pool_import events. 17eda14cbcSMatt Macy# 18eda14cbcSMatt Macy# Note that this script requires that your enclosure be supported by the 1916038816SMartin Matuska# Linux SCSI Enclosure services (SES) driver. The script will do nothing 20eda14cbcSMatt Macy# if you have no enclosure, or if your enclosure isn't supported. 21eda14cbcSMatt Macy# 22eda14cbcSMatt Macy# Exit codes: 23eda14cbcSMatt Macy# 0: enclosure led successfully set 24eda14cbcSMatt Macy# 1: enclosure leds not available 25eda14cbcSMatt Macy# 2: enclosure leds administratively disabled 26eda14cbcSMatt Macy# 3: The led sysfs path passed from ZFS does not exist 27eda14cbcSMatt Macy# 4: $ZPOOL not set 28eda14cbcSMatt Macy# 5: awk is not installed 29eda14cbcSMatt Macy 30eda14cbcSMatt Macy[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc" 31eda14cbcSMatt Macy. "${ZED_ZEDLET_DIR}/zed-functions.sh" 32eda14cbcSMatt Macy 3381b22a98SMartin Matuskaif [ ! -d /sys/class/enclosure ] && [ ! -d /sys/bus/pci/slots ] ; then 3481b22a98SMartin Matuska # No JBOD enclosure or NVMe slots 35eda14cbcSMatt Macy exit 1 36eda14cbcSMatt Macyfi 37eda14cbcSMatt Macy 38eda14cbcSMatt Macyif [ "${ZED_USE_ENCLOSURE_LEDS}" != "1" ] ; then 39eda14cbcSMatt Macy exit 2 40eda14cbcSMatt Macyfi 41eda14cbcSMatt Macy 42eda14cbcSMatt Macyzed_check_cmd "$ZPOOL" || exit 4 43eda14cbcSMatt Macyzed_check_cmd awk || exit 5 44eda14cbcSMatt Macy 45eda14cbcSMatt Macy# Global used in set_led debug print 46eda14cbcSMatt Macyvdev="" 47eda14cbcSMatt Macy 48eda14cbcSMatt Macy# check_and_set_led (file, val) 49eda14cbcSMatt Macy# 50eda14cbcSMatt Macy# Read an enclosure sysfs file, and write it if it's not already set to 'val' 51eda14cbcSMatt Macy# 52eda14cbcSMatt Macy# Arguments 53eda14cbcSMatt Macy# file: sysfs file to set (like /sys/class/enclosure/0:0:1:0/SLOT 10/fault) 54eda14cbcSMatt Macy# val: value to set it to 55eda14cbcSMatt Macy# 56eda14cbcSMatt Macy# Return 57eda14cbcSMatt Macy# 0 on success, 3 on missing sysfs path 58eda14cbcSMatt Macy# 59eda14cbcSMatt Macycheck_and_set_led() 60eda14cbcSMatt Macy{ 61eda14cbcSMatt Macy file="$1" 62eda14cbcSMatt Macy val="$2" 63eda14cbcSMatt Macy 6416038816SMartin Matuska if [ -z "$val" ]; then 6516038816SMartin Matuska return 0 6616038816SMartin Matuska fi 6716038816SMartin Matuska 68eda14cbcSMatt Macy if [ ! -e "$file" ] ; then 69eda14cbcSMatt Macy return 3 70eda14cbcSMatt Macy fi 71eda14cbcSMatt Macy 72eda14cbcSMatt Macy # If another process is accessing the LED when we attempt to update it, 73eda14cbcSMatt Macy # the update will be lost so retry until the LED actually changes or we 74eda14cbcSMatt Macy # timeout. 7516038816SMartin Matuska for _ in 1 2 3 4 5; do 76eda14cbcSMatt Macy # We want to check the current state first, since writing to the 77eda14cbcSMatt Macy # 'fault' entry always causes a SES command, even if the 78eda14cbcSMatt Macy # current state is already what you want. 7916038816SMartin Matuska read -r current < "${file}" 80eda14cbcSMatt Macy 81eda14cbcSMatt Macy # On some enclosures if you write 1 to fault, and read it back, 82eda14cbcSMatt Macy # it will return 2. Treat all non-zero values as 1 for 83eda14cbcSMatt Macy # simplicity. 84eda14cbcSMatt Macy if [ "$current" != "0" ] ; then 85eda14cbcSMatt Macy current=1 86eda14cbcSMatt Macy fi 87eda14cbcSMatt Macy 88eda14cbcSMatt Macy if [ "$current" != "$val" ] ; then 89eda14cbcSMatt Macy echo "$val" > "$file" 90eda14cbcSMatt Macy zed_log_msg "vdev $vdev set '$file' LED to $val" 91eda14cbcSMatt Macy else 92eda14cbcSMatt Macy break 93eda14cbcSMatt Macy fi 94eda14cbcSMatt Macy done 95eda14cbcSMatt Macy} 96eda14cbcSMatt Macy 9781b22a98SMartin Matuska# Fault LEDs for JBODs and NVMe drives are handled a little differently. 9881b22a98SMartin Matuska# 9981b22a98SMartin Matuska# On JBODs the fault LED is called 'fault' and on a path like this: 10081b22a98SMartin Matuska# 10181b22a98SMartin Matuska# /sys/class/enclosure/0:0:1:0/SLOT 10/fault 10281b22a98SMartin Matuska# 10381b22a98SMartin Matuska# On NVMe it's called 'attention' and on a path like this: 10481b22a98SMartin Matuska# 10581b22a98SMartin Matuska# /sys/bus/pci/slot/0/attention 10681b22a98SMartin Matuska# 10781b22a98SMartin Matuska# This function returns the full path to the fault LED file for a given 10881b22a98SMartin Matuska# enclosure/slot directory. 10981b22a98SMartin Matuska# 11081b22a98SMartin Matuskapath_to_led() 11181b22a98SMartin Matuska{ 11281b22a98SMartin Matuska dir=$1 11381b22a98SMartin Matuska if [ -f "$dir/fault" ] ; then 11481b22a98SMartin Matuska echo "$dir/fault" 11581b22a98SMartin Matuska elif [ -f "$dir/attention" ] ; then 11681b22a98SMartin Matuska echo "$dir/attention" 11781b22a98SMartin Matuska fi 11881b22a98SMartin Matuska} 11981b22a98SMartin Matuska 120eda14cbcSMatt Macystate_to_val() 121eda14cbcSMatt Macy{ 122eda14cbcSMatt Macy state="$1" 12316038816SMartin Matuska case "$state" in 124*cbfe9975SMartin Matuska FAULTED|DEGRADED|UNAVAIL|REMOVED) 125eda14cbcSMatt Macy echo 1 12616038816SMartin Matuska ;; 12716038816SMartin Matuska ONLINE) 128eda14cbcSMatt Macy echo 0 12916038816SMartin Matuska ;; 130e92ffd9bSMartin Matuska *) 131e92ffd9bSMartin Matuska echo "invalid state: $state" 132e92ffd9bSMartin Matuska ;; 13316038816SMartin Matuska esac 134eda14cbcSMatt Macy} 135eda14cbcSMatt Macy 13681b22a98SMartin Matuska# 13781b22a98SMartin Matuska# Given a nvme name like 'nvme0n1', pass back its slot directory 13881b22a98SMartin Matuska# like "/sys/bus/pci/slots/0" 13981b22a98SMartin Matuska# 14081b22a98SMartin Matuskanvme_dev_to_slot() 14181b22a98SMartin Matuska{ 14281b22a98SMartin Matuska dev="$1" 14381b22a98SMartin Matuska 14481b22a98SMartin Matuska # Get the address "0000:01:00.0" 145e92ffd9bSMartin Matuska read -r address < "/sys/class/block/$dev/device/address" 14681b22a98SMartin Matuska 147e92ffd9bSMartin Matuska find /sys/bus/pci/slots -regex '.*/[0-9]+/address$' | \ 148e92ffd9bSMartin Matuska while read -r sys_addr; do 149e92ffd9bSMartin Matuska read -r this_address < "$sys_addr" 15081b22a98SMartin Matuska 15181b22a98SMartin Matuska # The format of address is a little different between 15281b22a98SMartin Matuska # /sys/class/block/$dev/device/address and 15381b22a98SMartin Matuska # /sys/bus/pci/slots/ 15481b22a98SMartin Matuska # 15581b22a98SMartin Matuska # address= "0000:01:00.0" 15681b22a98SMartin Matuska # this_address = "0000:01:00" 15781b22a98SMartin Matuska # 15881b22a98SMartin Matuska if echo "$address" | grep -Eq ^"$this_address" ; then 159e92ffd9bSMartin Matuska echo "${sys_addr%/*}" 16081b22a98SMartin Matuska break 16181b22a98SMartin Matuska fi 16281b22a98SMartin Matuska done 16381b22a98SMartin Matuska} 16481b22a98SMartin Matuska 16581b22a98SMartin Matuska 16616038816SMartin Matuska# process_pool (pool) 167eda14cbcSMatt Macy# 16816038816SMartin Matuska# Iterate through a pool and set the vdevs' enclosure slot LEDs to 16916038816SMartin Matuska# those vdevs' state. 170eda14cbcSMatt Macy# 171eda14cbcSMatt Macy# Arguments 17216038816SMartin Matuska# pool: Pool name. 173eda14cbcSMatt Macy# 174eda14cbcSMatt Macy# Return 175eda14cbcSMatt Macy# 0 on success, 3 on missing sysfs path 176eda14cbcSMatt Macy# 177eda14cbcSMatt Macyprocess_pool() 178eda14cbcSMatt Macy{ 179eda14cbcSMatt Macy pool="$1" 18016038816SMartin Matuska 18116038816SMartin Matuska # The output will be the vdevs only (from "grep '/dev/'"): 18216038816SMartin Matuska # 18316038816SMartin Matuska # U45 ONLINE 0 0 0 /dev/sdk 0 18416038816SMartin Matuska # U46 ONLINE 0 0 0 /dev/sdm 0 18516038816SMartin Matuska # U47 ONLINE 0 0 0 /dev/sdn 0 18616038816SMartin Matuska # U50 ONLINE 0 0 0 /dev/sdbn 0 18716038816SMartin Matuska # 18816038816SMartin Matuska ZPOOL_SCRIPTS_AS_ROOT=1 $ZPOOL status -c upath,fault_led "$pool" | grep '/dev/' | ( 189eda14cbcSMatt Macy rc=0 19016038816SMartin Matuska while read -r vdev state _ _ _ therest; do 191eda14cbcSMatt Macy # Read out current LED value and path 19216038816SMartin Matuska # Get dev name (like 'sda') 19316038816SMartin Matuska dev=$(basename "$(echo "$therest" | awk '{print $(NF-1)}')") 19416038816SMartin Matuska vdev_enc_sysfs_path=$(realpath "/sys/class/block/$dev/device/enclosure_device"*) 19581b22a98SMartin Matuska if [ ! -d "$vdev_enc_sysfs_path" ] ; then 19681b22a98SMartin Matuska # This is not a JBOD disk, but it could be a PCI NVMe drive 19781b22a98SMartin Matuska vdev_enc_sysfs_path=$(nvme_dev_to_slot "$dev") 19881b22a98SMartin Matuska fi 19981b22a98SMartin Matuska 20016038816SMartin Matuska current_val=$(echo "$therest" | awk '{print $NF}') 201eda14cbcSMatt Macy 202eda14cbcSMatt Macy if [ "$current_val" != "0" ] ; then 203eda14cbcSMatt Macy current_val=1 204eda14cbcSMatt Macy fi 205eda14cbcSMatt Macy 206eda14cbcSMatt Macy if [ -z "$vdev_enc_sysfs_path" ] ; then 207eda14cbcSMatt Macy # Skip anything with no sysfs LED entries 208eda14cbcSMatt Macy continue 209eda14cbcSMatt Macy fi 210eda14cbcSMatt Macy 21181b22a98SMartin Matuska led_path=$(path_to_led "$vdev_enc_sysfs_path") 21281b22a98SMartin Matuska if [ ! -e "$led_path" ] ; then 21316038816SMartin Matuska rc=3 21481b22a98SMartin Matuska zed_log_msg "vdev $vdev '$led_path' doesn't exist" 21516038816SMartin Matuska continue 216eda14cbcSMatt Macy fi 217eda14cbcSMatt Macy 218eda14cbcSMatt Macy val=$(state_to_val "$state") 219eda14cbcSMatt Macy 220eda14cbcSMatt Macy if [ "$current_val" = "$val" ] ; then 221eda14cbcSMatt Macy # LED is already set correctly 22216038816SMartin Matuska continue 223eda14cbcSMatt Macy fi 224eda14cbcSMatt Macy 22581b22a98SMartin Matuska if ! check_and_set_led "$led_path" "$val"; then 22616038816SMartin Matuska rc=3 227eda14cbcSMatt Macy fi 228eda14cbcSMatt Macy done 22916038816SMartin Matuska exit "$rc"; ) 230eda14cbcSMatt Macy} 231eda14cbcSMatt Macy 232eda14cbcSMatt Macyif [ -n "$ZEVENT_VDEV_ENC_SYSFS_PATH" ] && [ -n "$ZEVENT_VDEV_STATE_STR" ] ; then 23316038816SMartin Matuska # Got a statechange for an individual vdev 234eda14cbcSMatt Macy val=$(state_to_val "$ZEVENT_VDEV_STATE_STR") 235eda14cbcSMatt Macy vdev=$(basename "$ZEVENT_VDEV_PATH") 23681b22a98SMartin Matuska ledpath=$(path_to_led "$ZEVENT_VDEV_ENC_SYSFS_PATH") 23781b22a98SMartin Matuska check_and_set_led "$ledpath" "$val" 238eda14cbcSMatt Macyelse 239eda14cbcSMatt Macy # Process the entire pool 240eda14cbcSMatt Macy poolname=$(zed_guid_to_pool "$ZEVENT_POOL_GUID") 241eda14cbcSMatt Macy process_pool "$poolname" 242eda14cbcSMatt Macyfi 243