xref: /spdk/test/vhost/irqs.sh (revision f8a085a2d5197a152d0d3d43b6d34f80d34eb2ef)
1#  SPDX-License-Identifier: BSD-3-Clause
2#  Copyright (C) 2022 Intel Corporation.
3#  All rights reserved.
4
5source "$rootdir/test/scheduler/common.sh"
6
7declare -a irqs_counters=()
8declare -a irqs_devices=()
9declare -a irqs_types=()
10declare -a cpus=()
11
12_get_cpus() {
13	local state=${1:-online}
14
15	_get_override_cpus || "_get_${state}_cpus"
16}
17
18_get_override_cpus() {
19	((${#cpus_override[@]} > 0)) || return 1
20	fold_list_onto_array cpus "${cpus_override[@]}"
21}
22
23_get_online_cpus() {
24	fold_list_onto_array cpus $(get_online_cpus)
25}
26
27_get_offline_cpus() {
28	fold_list_onto_array cpus $(get_offline_cpus)
29}
30
31get_all_irqs() {
32	local irqs=() s_irqs=()
33
34	irqs=(/proc/irq/+([0-9]))
35	irqs=("${irqs[@]##*/}")
36
37	# Sort it
38	fold_list_onto_array s_irqs "${irqs[@]}"
39
40	printf '%u\n' "${s_irqs[@]}"
41}
42
43check_this_irq() {
44	local irq=${1:-0}
45
46	((${#irqs_to_lookup[@]} > 0)) || return 0
47
48	[[ -n ${irqs_to_lookup[irq]} ]]
49}
50
51update_irqs_sysfs() {
52	local sysroot=${1:-}
53	local input=$sysroot/sys/kernel/irq
54
55	[[ -e $input ]] || return 1
56
57	# per_cpu_count holds a list of all cpus, regardless of their state
58	_get_cpus online
59	_get_cpus offline
60
61	local irq_path
62	for irq_path in "$input/"*; do
63		irq=${irq_path##*/}
64		check_this_irq "$irq" || continue
65		IFS="," read -ra cpu_counters < "$irq_path/per_cpu_count"
66		for cpu in "${cpus[@]}"; do
67			eval "_irq${irq}_cpu${cpu}+=(${cpu_counters[cpu]})"
68			eval "_irq${irq}_counter[cpu]=_irq${irq}_cpu${cpu}[@]"
69		done
70		irqs_counters[irq]="_irq${irq}_counter[@]"
71		irqs_devices[irq]=$(< "$irq_path/actions")
72		irqs_types[irq]="$(< "$irq_path/chip_name") $(< "$irq_path/hwirq")-$(< "$irq_path/name")"
73	done
74}
75
76update_irqs_procfs() {
77	local input=${1:-/proc/interrupts}
78
79	[[ -e $input ]] || return 1
80
81	# /proc/interrupts shows only online CPUs. Use get_cpus() to get readings for
82	# proper CPUs rather than parsing the actual header of the file.
83	_get_cpus online
84
85	local counter_idx
86	while read -ra irqs; do
87		irq=${irqs[0]%:*}
88		[[ $irq == +([0-9]) ]] || continue
89		check_this_irq "$irq" || continue
90		cpu_counters=("${irqs[@]:1:${#cpus[@]}}") counter_idx=0
91		for cpu in "${cpus[@]}"; do
92			eval "_irq${irq}_cpu${cpu}+=(${cpu_counters[counter_idx++]})"
93			eval "_irq${irq}_counter[cpu]=_irq${irq}_cpu${cpu}[@]"
94		done
95		irqs_counters[irq]="_irq${irq}_counter[@]"
96		irqs_devices[irq]=${irqs[*]:${#cpus[@]}+1:2}
97		irqs_types[irq]=${irqs[*]:${#cpus[@]}+3}
98	done < "$input"
99}
100
101update_irqs() {
102	local irqs irq
103	local cpu cpu_counters=()
104	local irqs_to_lookup=()
105
106	fold_list_onto_array irqs_to_lookup "$@"
107
108	update_irqs_sysfs || update_irqs_procfs
109}
110
111get_irqs() {
112	local irqs=("$@") irq cpu
113	local _counters counters counter delta total
114
115	# If cpus[@] are not init, update was not run so nothing to check
116	((${#cpus[@]} > 0)) || return 1
117
118	if ((${#irqs[@]} == 0)); then
119		irqs=($(get_all_irqs))
120	fi
121
122	for irq in "${irqs[@]}"; do
123		for cpu in "${cpus[@]}"; do
124			[[ -v "_irq${irq}_cpu${cpu}[@]" ]] || continue
125			local -n counters="_irq${irq}_cpu${cpu}"
126			# keep a separate copy to not touch the main _irq*[]
127			_counters=("${counters[@]}") total=0
128
129			if ((${#counters[@]} > 1)); then
130				# Enhance output with calculating deltas between each reading
131				for ((counter = 0; counter < ${#counters[@]} - 1; counter++)); do
132					delta=$((counters[counter + 1] - counters[counter]))
133					_counters[counter + 1]="${counters[counter + 1]} (+$delta)"
134					: $((total += delta))
135				done
136			fi
137			_counters+=("==$total")
138			# Ignore idle irqs unless request from the env tells otherwise
139			if [[ -n $SHOW_ALL_IRQS ]] || ((total > 0)); then
140				echo "irq$irq->$(get_irq_type "$irq")->$(get_irq_device "$irq")@cpu$cpu:"
141				printf '  %s\n' "${_counters[@]}"
142			fi
143		done
144	done
145}
146
147get_irq_type() {
148	[[ -n ${irqs_types[$1]} ]] || return 1
149	echo "${irqs_types[$1]}"
150}
151
152get_irq_device() {
153	[[ -n ${irqs_devices[$1]} ]] || return 1
154	echo "${irqs_devices[$1]}"
155}
156
157reset_irqs() {
158	irqs_counters=() irqs_devices=() irqs_types=() cpus=()
159
160	unset -v "${!_irq@}"
161}
162
163read_irq_cpu_mask() {
164	local irq=$1 mask=$2
165
166	[[ -n $irq && -e /proc/irq/$irq || -n $mask ]] || return 1
167
168	# smp_affinity holds a string of comma-separated 32-bit values. Iterate
169	# over each dWORD and extract cpus bit by bit. Iterate from the end of
170	# the array as that's where the first dWORD is located.
171	local smp_affinity
172	local bit dword dword_l=32 dword_idx
173	local cpus=()
174
175	if [[ -n $mask ]]; then
176		IFS="," read -ra smp_affinity <<< "$mask"
177	else
178		IFS="," read -ra smp_affinity < "/proc/irq/$irq/smp_affinity"
179	fi
180
181	smp_affinity=("${smp_affinity[@]/#/0x}")
182
183	for ((dword = ${#smp_affinity[@]} - 1, dword_idx = 0; dword >= 0; dword--, dword_idx++)); do
184		bit=-1
185		while ((++bit < dword_l)); do
186			if ((smp_affinity[dword] & 1 << bit)); then
187				cpus[bit + dword_l * dword_idx]=$bit
188			fi
189		done
190	done
191
192	printf '%u\n' "${!cpus[@]}"
193}
194
195read_irq_cpu_list() {
196	local irq=$1 effective=${2:-0}
197
198	[[ -n $irq && -e /proc/irq/$irq ]] || return 1
199
200	if ((effective)); then
201		parse_cpu_list "/proc/irq/$irq/effective_affinity_list"
202	else
203		parse_cpu_list "/proc/irq/$irq/smp_affinity_list"
204	fi
205}
206
207build_irq_cpu_mask() {
208	local cpu dword_l=32 dword_idx dword idxs
209	local _mask=() mask=""
210
211	for cpu; do
212		dword_idx=$((cpu / dword_l))
213		((_mask[dword_idx] |= 1 << (cpu - dword_l * dword_idx)))
214	done
215
216	# Store sorted list of dword indexes that we got
217	idxs=("${!_mask[@]}")
218
219	# Fill out all dWORDs starting from the highest (last) dword index
220	for ((dword = idxs[-1]; dword >= 0; dword--)); do
221		_mask[dword]=$(printf '%08x' "${_mask[dword]}")
222		mask=${mask:+$mask,}${_mask[dword]}
223	done
224
225	echo "$mask"
226}
227
228squash_irq_cpu_mask() {
229	local mask
230
231	mask=$(build_irq_cpu_mask "$@")
232	# E.g.: 1,32,64,65,77,88,127 -> 0x80000000010020030000000100000002
233	# Valid under DPDK
234	echo "0x${mask//,/}"
235}
236
237unsquash_irq_cpu_mask() {
238	# E.g.: 0x80000000010020030000000100000002 -> 80000000,01002003,00000001,00000002
239	# 8 is a max number of chars in a dWORD represented in hex.
240
241	local smask=$1 _smask="" smask_l _smask_l
242
243	smask=${smask/0x/} smask_l=$((${#smask} / 8)) _smask_l=$smask_l
244
245	((smask_l == 0)) && echo "$smask" && return 0
246
247	# Put comma at a right index
248	while ((_smask_l)); do
249		_smask+=,${smask:${#smask}-_smask_l--*8:8}
250	done
251
252	# Add remaining chars if any
253	_smask=${smask::${#smask}-8*smask_l}${_smask}
254
255	# If there were no chars left, drop the ',' from the beginning of the string
256	echo "${_smask#,}"
257}
258