#!/bin/bash # SPDX-License-Identifier: MIT # # chreipl-fcp-mpath: use multipath information to change FCP IPL target # (C) Copyright IBM Corp. 2021 # # Uses the following system-utilities (and shell-builtins): # Those necessary for sourced library: # - chreipl-fcp-mpath-common.sh # GNU coreutils: # - truncate # util-linux: # - hexdump # Try to change the current re-IPL target to a dfferent, operational path to # the same volume. # # Makes use of udev event environment variables: # DM_UUID # SUBSYSTEM # DEVPATH # CHREIPL_FCP_MPATH_IS_TGT # shellcheck disable=SC2034 declare -gr debug_trace_tag=15tcip # shellcheck disable=SC1091 source '@chreiplzfcpmp-lib@' || exit 127 function apply_ipl_information() { local sdev_wwid="${1}" local sdev_busid="${2}" sdev_wwpn="${3}" sdev_lun="${4}" local ipl_type="${5}" ipl_busid="${6}" ipl_wwpn="${7}" ipl_lun="${8}" local -a records local try_update_id_file=true [ "${ipl_type}" = "fcp" ] || return 1 [[ "${sdev_busid}" =~ ^[[:xdigit:]]{1,3}\.[[:xdigit:]]\.[[:xdigit:]]{1,4}$ ]] \ || return 2 [[ "${sdev_wwpn}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 3 [[ "${sdev_lun}" =~ ^0x[[:xdigit:]]{16}$ ]] || return 4 # After updating the firmware re-IPL information below we also try to # update the information stored in the ID file (necessary, so it # contains the correct Device-Bus-ID/WWPN/LUN after the update). For # the update of the ID file we try to grab an exclusive lock, so there # are no overlapping reads/writes. # # In case we can't get the lock because the ID file is missing, but we # have a direct TGT match, we may still try to change the re-IPL # information, but skip the ID file update. # # "direct match" means, the event subject is either the SDEV that is # currently set as re-IPL target, or it is the dm-multipath device that # currently contains the re-IPL target. if ! id_file_lock_exclusive_no_create; then # rc == 1 --> could not read ${ID_FILE} [ "${PIPESTATUS[0]}" -eq 1 ] || return 5 # if true, we are dealing with a direct TGT match [ "${CHREIPL_FCP_MPATH_IS_TGT}" = "true" ] || return 6 try_update_id_file=false fi # If we have a direct match (see in the comment above), we know # that we have a path to the current re-IPL volume - no matter of the # WWID. Otherwise, we got here by comparing the WWID of the event # subject with the one recorded in the ID file; in this case we try to # make sure the information is still up-to-date. if [ "${CHREIPL_FCP_MPATH_IS_TGT}" != "true" ]; then # last bail to make sure we don't overwrite user choices.. # # XXX: this will *NOT* prevent the race completely, but at least # make it less likely { readarray -d "" -t -u "${ID_FILE_LOCK}" records; } 2>/dev/null \ || return 7 if '@DEBUG@'; then declare -p records 1>&2; fi [ "${#records[@]}" = "4" ] || return 8 [ "${records[0]}" = "${sdev_wwid}" ] || return 9 [ "${records[1]}" = "${ipl_busid}" ] || return 10 [ "${records[2]}" = "${ipl_wwpn}" ] || return 11 [ "${records[3]}" = "${ipl_lun}" ] || return 12 fi # Take lock so we don't see any intermediate state from other helpers # running in parallel firmware_lock_exclusive || return 13 if ! { echo "${sdev_busid}" >| /sys/firmware/reipl/fcp/device \ && echo "${sdev_wwpn}" >| /sys/firmware/reipl/fcp/wwpn \ && echo "${sdev_lun}" >| /sys/firmware/reipl/fcp/lun; }; then log_alert "Changing the re-IPL device failed. The current re-IPL settings might be inconsistent. Check and correct the settings (see the README.md of chreipl-fcp-mpath) to make sure that the current re-IPL device is valid." return 14 fi firmware_unlock_exclusive if [ "${sdev_busid}" != "${ipl_busid}" ] \ || [ "${sdev_wwpn}" != "${ipl_wwpn}" ] \ || [ "${sdev_lun}" != "${ipl_lun}" ]; then log_note "Changed re-IPL path to: ${sdev_busid}:${sdev_wwpn}:${sdev_lun}." fi # Try to update the information in the ID file if we have gotten the # lock for it. if ${try_update_id_file}; then # reset ID without removing the file truncate --no-create --size=0 "${ID_FILE}" || return 15 echo -ne "${sdev_wwid}\x00${sdev_busid}\x00${sdev_wwpn}\x00${sdev_lun}\x00" \ >>"${ID_FILE}" || return 16 id_file_unlock_exclusive_no_create if '@DEBUG@'; then hexdump -vC "${ID_FILE}" 1>&2; fi fi return 0 } declare -g SDEV="" if [[ "${DM_UUID}" == mpath-* ]]; then # Assume Multipath Device Mapper Device; # e.g.: DEVPATH = /devices/virtual/block/dm-0 for sdev in /sys/"${DEVPATH}"/slaves/sd*/device; do if sdev_test_path_state "${sdev}"; then SDEV="${sdev}" break fi done # No path of the multipath-device that represents the IPL volume is # online. if [ "${SDEV}" = "" ]; then log_crit "The re-IPL device cannot be changed because no operational path to the re-IPL volume remains. The next re-IPL might fail unless you re-attach or enable at least one valid path to the re-IPL volume." fi elif [ "${SUBSYSTEM}" = block ]; then # Assume SCSI Disk; # e.g.: DEVPATH = /devices/css0/0.0.0014/0.0.1700/host0/rport-0:0-0/target0:0:0/0:0:0:1074806808/block/sds if sdev_test_path_state /sys/"${DEVPATH}"/device; then SDEV=/sys/"${DEVPATH}"/device fi fi [ "${SDEV}" != "" ] || exit 0 sdev_get_wwid "${SDEV}" || exit 0 sdev_get_fcp_addressing "${SDEV}" || exit 0 firmware_get_ipl_information || exit 0 # shellcheck disable=SC2153 apply_ipl_information \ "${SDEV_WWID}" "${SDEV_BUSID}" "${SDEV_WWPN}" "${SDEV_LUN}" \ "${IPL_TYPE}" "${IPL_BUSID}" "${IPL_WWPN}" "${IPL_LUN}" || exit 0 exit 0