xref: /spdk/scripts/common/setup/interactive.sh (revision 12fbe739a31b09aff0d05f354d4f3bbef99afc55)
1#!/usr/bin/env bash
2#  SPDX-License-Identifier: BSD-3-Clause
3#  Copyright (C) 2023 Intel Corporation
4#  All rights reserved.
5#
6set +e
7
8yn() {
9	local -A yn=()
10	local _yn
11
12	yn["y"]=0 yn["n"]=1
13
14	while read -rp "$* (y|N)> " _yn || true; do
15		_yn=${_yn::1} _yn=${_yn,,} _yn=${_yn:-n}
16		[[ -n ${yn["$_yn"]} ]] && return "${yn["$_yn"]}"
17	done
18}
19
20elevate() {
21	((UID != 0)) || return 0
22
23	if yn "You ($UID) need to be root to commit any changes. Elevate privileges?"; then
24		exec sudo -E "$rootdir/scripts/setup.sh" interactive "$@"
25	fi
26}
27
28stdin() {
29	[[ ! -t 0 ]] || return 0
30
31	echo "Requested interactive mode but stdin is not attached to a terminal, bailing" >&2
32	return 1
33}
34
35main_menu() {
36	local type=all answer quick_mode=$1
37
38	stdin || return 1
39	elevate "$quick_mode"
40
41	case "${quick_mode,,}" in
42		config | reset)
43			editor odevices quick && mode=$quick_mode
44			return
45			;;
46	esac
47
48	while ((1)); do
49		cat <<- MENU
50
51			1) List PCI Devices [Currently Listing: "$type"]
52			2) Change Devices To List
53			3) Mark Device As Blocked (${PCI_BLOCKED:-none})
54			4) Mark Device As Allowed (${PCI_ALLOWED:-none})
55			5) Override Device In-Use Status
56			$([[ $os == Linux ]] && echo "6) Bind Device")
57
58			c) configure
59			s) status
60			r) reset
61			$([[ $os == Linux ]] && echo "hp) hugepages")
62
63			Q) Quit
64			U) Update Devices View
65
66		MENU
67
68		read -rp "> " answer || answer=q
69
70		case "${answer,,}" in
71			1) pdevices ;;
72			2) ctype && pdevices ;;
73			3) fdevices 0 ;;
74			4) fdevices 1 ;;
75			5) odevices ;;
76			5e) editor odevices ;;
77			6) bdevices ;;
78			q) yn "Are you sure you want to quit?" && return 1 ;;
79			c | commit | config)
80				yn "Are you sure you want jump to config mode?" || continue
81				mode=config
82				return
83				;;
84			s | status) status ;;
85			r | reset)
86				yn "Are you sure you want jump to reset mode?" || continue
87				mode=reset
88				return
89				;;
90			u | update)
91				CMD=reset cache_pci_bus
92				collect_devices
93				;;
94			hp) hugepages ;;
95		esac
96	done
97}
98
99gdevices() {
100	if [[ $type == all ]]; then
101		local -gn dev_ref=all_devices_d
102	else
103		local -gn dev_ref=${type}_d
104	fi
105}
106
107pdevices() {
108	gdevices
109
110	local set_marker=$1
111	local use_map=()
112	local -A markers=()
113
114	use_map[0]="not used" use_map[1]="used"
115	markers["not used"]=pick markers["used"]=skip
116
117	if ((${#dev_ref[@]} == 0)); then
118		echo "No devices found"
119	else
120		for dev in "${!dev_ref[@]}"; do
121			printf '%s- %s [%s, %s] (%s@%s:%s)\n' \
122				"${set_marker:+${markers["${use_map[all_devices_d["$dev"]]}"]} }" \
123				"$dev" "${use_map[all_devices_d["$dev"]]}" "${drivers_d["$dev"]:-none}" \
124				"${all_devices_type_d["$dev"]}" \
125				"${pci_ids_vendor["$dev"]}" \
126				"${pci_ids_device["$dev"]}"
127		done
128	fi
129}
130
131ctype() {
132	local type_to_set
133	local -n types_ref=types_d
134
135	while read -rp "(${!types_ref[*]} all)> " type_to_set; do
136		type_to_set=${type_to_set,,}
137		if [[ -z $type_to_set ]]; then
138			return
139		elif [[ -n ${types_ref["$type_to_set"]} || $type_to_set == all ]]; then
140			type=$type_to_set
141			return
142		fi
143	done
144}
145
146fdevices() {
147	local action=${1:-0} bdf
148	local am=()
149	local -gA action_0 action_1
150
151	am[0]=PCI_BLOCKED am[1]=PCI_ALLOWED
152
153	gdevices
154	local -n action_ref=action_${action}
155	local -n action_ref_rev=action_$((!action))
156
157	while read -rp "(${!am[action]:-BDF})> " bdf; do
158		bdf=${bdf,,}
159		if [[ -z $bdf ]]; then
160			return
161		elif [[ -n ${dev_ref["$bdf"]} ]]; then
162			if [[ -n ${action_ref["$bdf"]} ]]; then
163				unset -v "action_ref[$bdf]"
164			else
165				action_ref["$bdf"]=1
166				unset -v "action_ref_rev[$bdf]"
167			fi
168			eval "${am[action]}='${!action_ref[*]}'"
169			eval "${am[!action]}='${!action_ref_rev[*]}'"
170		elif [[ -z ${dev_ref["$bdf"]} ]]; then
171			unset -v "action_ref[$bdf]"
172			eval "${am[action]}='${!action_ref[*]}'"
173		fi
174	done
175}
176
177editor() {
178	local devs_list=() devs_picked=() devs_skipped=()
179	local editor=${VISUAL:-${EDITOR:-vim}}
180	local tmp_file
181
182	type -P "$editor" > /dev/null || return
183
184	mapfile -t devs_list < <(pdevices markers)
185
186	tmp_file=$(mktemp -u)
187	cat <<- ODEVICES > "$tmp_file" || return
188		# Listing '$type' devices
189		#   Devices marked as "used" (i.e. contains any data) will be skipped by default
190		#   Devices marked as "not used" (i.e. does not contain data) will be picked by default
191
192		$(printf '%s\n' "${devs_list[@]}")
193
194		# p, pick devices
195		# s, skip devices
196
197		$([[ $editor == vi?(m) ]] && echo "# :cq[!] to not save any changes")
198	ODEVICES
199
200	"$editor" "$tmp_file" || return
201	[[ -s $tmp_file ]] || return
202
203	local action dev _type
204	while read -r action _ dev _type; do
205		case "${action,,}" in
206			s | skip)
207				[[ $_type != *"not used"* ]] && continue
208				devs_skipped+=("$dev")
209				;;
210			p | pick)
211				[[ $_type == *"not used"* ]] && continue
212				devs_picked+=("$dev")
213				;;
214		esac
215	done < "$tmp_file"
216	rm "$tmp_file"
217
218	if [[ $2 == quick ]] && ((${#devs_picked[@]} > 0)); then
219		if ! yn "Detected data on some of the devices (${devs_picked[*]}). Continue?"; then
220			return 1
221		fi
222	fi
223
224	"$1" < <(printf '%s\n' "${devs_skipped[@]}" "${devs_picked[@]}")
225
226}
227
228odevices() {
229	local bdf
230
231	type=all gdevices
232
233	while read -rp "(BDF)> " bdf; do
234		bdf=${bdf,,}
235		if [[ -z $bdf ]]; then
236			return
237		elif [[ -n ${dev_ref["$bdf"]} ]]; then
238			dev_ref["$bdf"]=$((!dev_ref["$bdf"]))
239		fi
240	done
241}
242
243bdevices() {
244	[[ $os == Linux ]] || return 0
245
246	local bdfd bdf driver
247
248	gdevices
249
250	while read -rp "(BDF->driver)> " bdfd; do
251		bdfd=${bdfd,,}
252		if [[ -z $bdfd ]]; then
253			return
254		fi
255
256		bdf=${bdfd/->*/} driver=${bdfd/*->/}
257
258		if [[ $driver == "${drivers_d["$bdf"]}" ]]; then
259			echo "$bdf already bound to $driver"
260			continue
261		fi
262
263		if [[ -n ${dev_ref["$bdf"]} && -n $driver ]]; then
264			if yn "$bdf currently bound to ${drivers_d["$bdf"]:-none}. Bind to $driver?"; then
265				linux_bind_driver "$bdf" "$driver"
266			fi
267		fi
268	done
269}
270
271status() {
272	local _os=${os,,}
273
274	if [[ $(type -t "status_${_os}") == function ]]; then
275		"status_${_os}"
276	fi
277}
278
279hugepages() {
280	[[ $os == Linux ]] || return 0
281	local hp
282
283	HUGE_EVEN_ALLOC=no
284	while read -rp "('clear' 'even' 'commit' HUGEMEM[=$HUGEMEM MB])> " hp; do
285		hp=${hp,,}
286		if [[ -z $hp ]]; then
287			return
288		elif [[ $hp == clear ]]; then
289			clear_hugepages
290			return
291		elif [[ $hp == even ]]; then
292			HUGE_EVEN_ALLOC=yes
293		elif [[ $hp =~ ^[1-9][0-9]*$ ]]; then
294			NRHUGE=""
295			HUGEMEM=$hp
296		elif [[ $hp == commit ]]; then
297			set_hp
298			configure_linux_hugepages
299			return
300		fi
301	done
302}
303