xref: /freebsd/sys/contrib/openzfs/cmd/zvol_wait (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
1#!/bin/sh
2
3count_zvols() {
4	if [ -z "$zvols" ]; then
5		echo 0
6	else
7		echo "$zvols" | wc -l
8	fi
9}
10
11filter_out_zvols_with_links() {
12	echo "$zvols" | tr ' ' '+' | while read -r zvol; do
13		if ! [ -L "/dev/zvol/$zvol" ]; then
14			echo "$zvol"
15		fi
16	done | tr '+' ' '
17}
18
19filter_out_deleted_zvols() {
20	OIFS="$IFS"
21	IFS="
22"
23	# shellcheck disable=SC2086
24	zfs list -H -o name $zvols 2>/dev/null
25	IFS="$OIFS"
26}
27
28list_zvols() {
29	read -r default_volmode < /sys/module/zfs/parameters/zvol_volmode
30	zfs list -t volume -H -o \
31	    name,volmode,receive_resume_token,redact_snaps,keystatus |
32	    while IFS="	" read -r name volmode token redacted keystatus; do # IFS=\t here!
33
34		# /dev links are not created for zvols with volmode = "none",
35		# redacted zvols, or encrypted zvols for which the key has not
36		# been loaded.
37		[ "$volmode" = "none" ] && continue
38		[ "$volmode" = "default" ] && [ "$default_volmode" = "3" ] &&
39		    continue
40		[ "$redacted" = "-" ] || continue
41		[ "$keystatus" = "unavailable" ] && continue
42
43		# We also ignore partially received zvols if it is
44		# not an incremental receive, as those won't even have a block
45		# device minor node created yet.
46		if [ "$token" != "-" ]; then
47
48			# Incremental receives create an invisible clone that
49			# is not automatically displayed by zfs list.
50			if ! zfs list "$name/%recv" >/dev/null 2>&1; then
51				continue
52			fi
53		fi
54		echo "$name"
55	done
56}
57
58zvols=$(list_zvols)
59zvols_count=$(count_zvols)
60if [ "$zvols_count" -eq 0 ]; then
61	echo "No zvols found, nothing to do."
62	exit 0
63fi
64
65echo "Testing $zvols_count zvol links"
66
67outer_loop=0
68while [ "$outer_loop" -lt 20 ]; do
69	outer_loop=$((outer_loop + 1))
70
71	old_zvols_count=$(count_zvols)
72
73	inner_loop=0
74	while [ "$inner_loop" -lt 30 ]; do
75		inner_loop=$((inner_loop + 1))
76
77		zvols="$(filter_out_zvols_with_links)"
78
79		zvols_count=$(count_zvols)
80		if [ "$zvols_count" -eq 0 ]; then
81			echo "All zvol links are now present."
82			exit 0
83		fi
84		sleep 1
85	done
86
87	echo "Still waiting on $zvols_count zvol links ..."
88	#
89	# Although zvols should normally not be deleted at boot time,
90	# if that is the case then their links will be missing and
91	# we would stall.
92	#
93	if [ "$old_zvols_count" -eq "$zvols_count" ]; then
94		echo "No progress since last loop."
95		echo "Checking if any zvols were deleted."
96
97		zvols=$(filter_out_deleted_zvols)
98		zvols_count=$(count_zvols)
99
100		if [ "$old_zvols_count" -ne "$zvols_count" ]; then
101			echo "$((old_zvols_count - zvols_count)) zvol(s) deleted."
102		fi
103
104		if [ "$zvols_count" -ne 0 ]; then
105			echo "Remaining zvols:"
106			echo "$zvols"
107		else
108			echo "All zvol links are now present."
109			exit 0
110		fi
111	fi
112
113	#
114	# zvol_count made some progress - let's stay in this loop.
115	#
116	if [ "$old_zvols_count" -gt "$zvols_count" ]; then
117		outer_loop=$((outer_loop - 1))
118	fi
119done
120
121echo "Timed out waiting on zvol links"
122exit 1
123