xref: /spdk/scripts/sync_dev_uevents.sh (revision eb53c23236cccb6b698b7ca70ee783da1c574b5f)
14f1701f6SMichal Berger#!/usr/bin/env bash
2*eb53c232Spaul luse#  SPDX-License-Identifier: BSD-3-Clause
3*eb53c232Spaul luse#  Copyright (C) 2020 Intel Corporation
4*eb53c232Spaul luse#  All rights reserved.
5*eb53c232Spaul luse#
64f1701f6SMichal Bergershopt -s extglob
74f1701f6SMichal Berger
84f1701f6SMichal Bergerexec {err}>&2
94f1701f6SMichal Berger
104f1701f6SMichal Bergerhelp() {
114f1701f6SMichal Berger	cat <<- HELP
124f1701f6SMichal Berger		${0##*/}: subsystem dev [..devN]
134f1701f6SMichal Berger
144f1701f6SMichal Berger		Env:
154f1701f6SMichal Berger		  UEVENT_TIMEOUT    - how long to wait for sync - ${UEVENT_TIMEOUT:-10}s
164f1701f6SMichal Berger		  UEVENT_ACTION     - uevent action to match on - ${UEVENT_ACTION:-add}
174f1701f6SMichal Berger		  DEVPATH_LOOKUP    - check if given dev matches inside DEVPATH
184f1701f6SMichal Berger		  DEVPATH_SUBSYSTEM - subsystem given dev should match in DEVPATH
194f1701f6SMichal Berger	HELP
204f1701f6SMichal Berger}
214f1701f6SMichal Berger
224f1701f6SMichal Bergerget_uevent_attr() (
234f1701f6SMichal Berger	source "$1"
244f1701f6SMichal Berger
254f1701f6SMichal Berger	[[ -v $2 ]] && echo "${!2}"
264f1701f6SMichal Berger)
274f1701f6SMichal Berger
284f1701f6SMichal Bergerfilter_devs() {
294f1701f6SMichal Berger	local dev p_dev
304f1701f6SMichal Berger	local maj min type sub
314f1701f6SMichal Berger
324f1701f6SMichal Berger	for dev in "${!devs[@]}"; do
334f1701f6SMichal Berger		[[ -e /dev/${devs[dev]} ]] || continue
344f1701f6SMichal Berger		[[ -c /dev/${devs[dev]} ]] && type=char
354f1701f6SMichal Berger		[[ -b /dev/${devs[dev]} ]] && type=block
364f1701f6SMichal Berger		maj=$((0x$(stat --printf="%t" "/dev/${devs[dev]}")))
374f1701f6SMichal Berger		min=$((0x$(stat --printf="%T" "/dev/${devs[dev]}")))
384f1701f6SMichal Berger
394f1701f6SMichal Berger		p_dev=/sys/dev/$type/$maj:$min
404f1701f6SMichal Berger		if [[ -e $p_dev ]]; then
414f1701f6SMichal Berger			printf '/dev/%s\n' "${devs[dev]}"
424f1701f6SMichal Berger
434f1701f6SMichal Berger			type=$(get_uevent_attr "$p_dev/uevent" DEVTYPE)
444f1701f6SMichal Berger			sub=$(readlink -f "$p_dev/subsystem") sub=${sub##*/}
454f1701f6SMichal Berger			if [[ $sub != "${subsystem%%/*}" ]]; then
464f1701f6SMichal Berger				printf '  wrong subsystem specified (%s != %s)\n' \
474f1701f6SMichal Berger					"${subsystem%%/*}" "$sub"
484f1701f6SMichal Berger			fi >&2
494f1701f6SMichal Berger
504f1701f6SMichal Berger			if [[ ${subsystem##*/} != "$subsystem" && -n $type ]]; then
514f1701f6SMichal Berger				if [[ ${subsystem##*/} != "$type" ]]; then
524f1701f6SMichal Berger					printf '  wrong devtype specified (%s != %s)\n' \
534f1701f6SMichal Berger						"${subsystem##*/}" "$type"
544f1701f6SMichal Berger				fi
554f1701f6SMichal Berger			fi >&2
564f1701f6SMichal Berger
574f1701f6SMichal Berger			unset -v "devs[dev]"
584f1701f6SMichal Berger		fi
594f1701f6SMichal Berger	done
604f1701f6SMichal Berger}
614f1701f6SMichal Berger
624f1701f6SMichal Bergerlook_in_devpath() {
634f1701f6SMichal Berger	local find=$1
644f1701f6SMichal Berger	local path=$2
654f1701f6SMichal Berger	local sub
664f1701f6SMichal Berger
674f1701f6SMichal Berger	[[ -v DEVPATH_LOOKUP ]] || return 1
684f1701f6SMichal Berger
694f1701f6SMichal Berger	if [[ -z $path ]]; then
704f1701f6SMichal Berger		return 1
714f1701f6SMichal Berger	fi
724f1701f6SMichal Berger
734f1701f6SMichal Berger	if [[ -e $path/subsystem ]]; then
744f1701f6SMichal Berger		sub=$(readlink -f "$path/subsystem")
754f1701f6SMichal Berger		sub=${sub##*/}
764f1701f6SMichal Berger	fi
774f1701f6SMichal Berger
784f1701f6SMichal Berger	if [[ ${path##*/} == "$find" ]]; then
794f1701f6SMichal Berger		if [[ -n $DEVPATH_SUBSYSTEM ]]; then
804f1701f6SMichal Berger			[[ $DEVPATH_SUBSYSTEM == "$sub" ]] || return 1
814f1701f6SMichal Berger		fi
824f1701f6SMichal Berger		return 0
834f1701f6SMichal Berger	fi
844f1701f6SMichal Berger	look_in_devpath "$find" "${path%/*}"
854f1701f6SMichal Berger}
864f1701f6SMichal Berger
874f1701f6SMichal Bergerif (($# < 2)); then
884f1701f6SMichal Berger	help
894f1701f6SMichal Berger	exit 1
904f1701f6SMichal Bergerfi
914f1701f6SMichal Berger
924f1701f6SMichal Bergersubsystem=$1 devs=("${@:2}")
934f1701f6SMichal Bergertimeout=${UEVENT_TIMEOUT:-10}
944f1701f6SMichal Bergeraction=${UEVENT_ACTION:-add}
954f1701f6SMichal Berger
964f1701f6SMichal Bergerdevs=("${devs[@]#/dev/}")
974f1701f6SMichal Berger[[ $action == add ]] && filter_devs
984f1701f6SMichal Berger
994f1701f6SMichal Berger((${#devs[@]})) || exit 0
1004f1701f6SMichal Berger
1014f1701f6SMichal Bergerif [[ -S /run/udev/control ]]; then
1024f1701f6SMichal Berger	# systemd-udevd realm
1034f1701f6SMichal Berger
1044f1701f6SMichal Berger	# If devtmpfs is in place then all, e.g., block subsystem devices are going to
1054f1701f6SMichal Berger	# be handled directly by the kernel. Otherwise, link to udev events in case we
1064f1701f6SMichal Berger	# have some old udevd on board which is meant to mknod them instead.
1074f1701f6SMichal Berger	if [[ $(< /proc/mounts) == *"/dev devtmpfs"* ]]; then
1084f1701f6SMichal Berger		events+=(--kernel)
1094f1701f6SMichal Berger	else
1104f1701f6SMichal Berger		events+=(--udev)
1114f1701f6SMichal Berger	fi
1124f1701f6SMichal Berger
1134f1701f6SMichal Berger	if [[ $subsystem != all ]]; then
1144f1701f6SMichal Berger		events+=("--subsystem-match=$subsystem")
1154f1701f6SMichal Berger	fi
1164f1701f6SMichal Berger
1174f1701f6SMichal Berger	# This trap targets a subshell which forks udevadm monitor. Since
1184f1701f6SMichal Berger	# process substitution works in an async fashion, $$ won't wait
1194f1701f6SMichal Berger	# for it, leaving it's child unattended after the main loop breaks
120d88f8ed4SMichal Berger	# (udevadm won't exit on its own either). UPDATE: This is not true
121d88f8ed4SMichal Berger	# anymore for Bash >= 5.2 where executing process replaces the
122d88f8ed4SMichal Berger	# actual subshell so $! becomes the PID of the udevadm instance.
123d88f8ed4SMichal Berger	# To accommodate that, attempt to signal $! directly in case pkill
124d88f8ed4SMichal Berger	# fails.
125d88f8ed4SMichal Berger	trap '[[ ! -e /proc/$!/status ]] || pkill -P $! || kill $!' EXIT
1264f1701f6SMichal Berger	# Also, this will block while reading through a pipe with a timeout
1274f1701f6SMichal Berger	# after not receiving any input. stdbuf is used since udevadm always
1284f1701f6SMichal Berger	# line buffers the monitor output.
1294f1701f6SMichal Berger	while ((${#devs[@]} > 0)) && IFS="=" read -t"$timeout" -r k v; do
1304f1701f6SMichal Berger		if [[ $k == ACTION && $v == "$action" ]]; then
1314f1701f6SMichal Berger			look_for_devname=1
1324f1701f6SMichal Berger			continue
1334f1701f6SMichal Berger		fi
1344f1701f6SMichal Berger		if ((look_for_devname == 1)); then
1354f1701f6SMichal Berger			for dev in "${!devs[@]}"; do
1364f1701f6SMichal Berger				# Explicitly allow globbing of the rhs to allow more open matching.
1374f1701f6SMichal Berger				# shellcheck disable=SC2053
1384f1701f6SMichal Berger				if [[ ${v#/dev/} == ${devs[dev]} || ${v##*/} == ${devs[dev]##*/} ]] \
1394f1701f6SMichal Berger					|| look_in_devpath "${devs[dev]}" "/sys/$v"; then
1404f1701f6SMichal Berger					unset -v "devs[dev]"
1414f1701f6SMichal Berger					look_for_devname=0
1424f1701f6SMichal Berger				fi
1434f1701f6SMichal Berger			done
1444f1701f6SMichal Berger		fi
1454f1701f6SMichal Berger	done < <(stdbuf --output=0 udevadm monitor --property "${events[@]}")
1464f1701f6SMichal Berger	if ((${#devs[@]} > 0)); then
1474f1701f6SMichal Berger		printf '* Events for some %s devices (%s) were not caught, they may be missing\n' \
1484f1701f6SMichal Berger			"$subsystem" "${devs[*]}"
1494f1701f6SMichal Berger	fi >&"$err"
1504f1701f6SMichal Berger	exit 0
1514f1701f6SMichal Bergerelif [[ -e /sys/kernel/uevent_helper ]]; then
1524f1701f6SMichal Berger	# Check if someones uses mdev to serialize uevents. If yes, simply check
1534f1701f6SMichal Berger	# if they are in sync, no need to lookup specific devices in this case.
1544f1701f6SMichal Berger	# If not, fall through to plain sleep.
1554f1701f6SMichal Berger	# To quote some wisdom from gentoo:
1564f1701f6SMichal Berger	# "Even the craziest scenario deserves a fair chance".
1574f1701f6SMichal Berger
1584f1701f6SMichal Berger	helper=$(< /sys/kernel/uevent_helper)
1594f1701f6SMichal Berger	if [[ ${helper##*/} == mdev && -e /dev/mdev.seq ]]; then
1604f1701f6SMichal Berger		# mdev keeps count of the seqnums on its own on each execution
1614f1701f6SMichal Berger		# and saves the count under /dev/mdev.seq. This is then set to
1624f1701f6SMichal Berger		# + 1 after the uevents finally settled.
163238251a1SMichal Berger		while ((timeout-- && $(< /sys/kernel/uevent_seqnum) + 1 != $(< /dev/mdev.seq))); do
1644f1701f6SMichal Berger			sleep 1s
1654f1701f6SMichal Berger		done
1664f1701f6SMichal Berger		if ((timeout < 0)); then
1674f1701f6SMichal Berger			printf '* Events not synced in time, %s devices (%s) may be missing\n' \
1684f1701f6SMichal Berger				"$subsystem" "${devs[*]}"
1694f1701f6SMichal Berger		fi
1704f1701f6SMichal Berger		exit 0
1714f1701f6SMichal Berger	fi >&"$err"
1724f1701f6SMichal Bergerfi 2> /dev/null
1734f1701f6SMichal Berger
1744f1701f6SMichal Berger# Fallback, sleep and hope for the best
1754f1701f6SMichal Bergersleep "${timeout}s"
176