xref: /spdk/test/scheduler/common.sh (revision 7bc1134d6e34affdbfaaa4a41c1a5cc8f2d609de)
1eb53c232Spaul luse#  SPDX-License-Identifier: BSD-3-Clause
2eb53c232Spaul luse#  Copyright (C) 2020 Intel Corporation
3eb53c232Spaul luse#  All rights reserved.
4eb53c232Spaul luse#
5eb53c232Spaul luse
6c1318c5bSMichal Bergerdeclare -r sysfs_system=/sys/devices/system
7c1318c5bSMichal Bergerdeclare -r sysfs_cpu=$sysfs_system/cpu
8c1318c5bSMichal Bergerdeclare -r sysfs_node=$sysfs_system/node
9c1318c5bSMichal Berger
10049d587eSMichal Bergerdeclare -r scheduler=$rootdir/test/event/scheduler/scheduler
11af24a874SMichal Bergerdeclare plugin=scheduler_plugin
12049d587eSMichal Berger
136872c67eSMichal Bergersource "$rootdir/test/scheduler/cgroups.sh"
141fd9dccfSMichal Berger
15c1318c5bSMichal Bergerfold_list_onto_array() {
16c1318c5bSMichal Berger	local array=$1
17c1318c5bSMichal Berger	local elem
18c1318c5bSMichal Berger
19c1318c5bSMichal Berger	shift || return 0
20c1318c5bSMichal Berger
21c1318c5bSMichal Berger	for elem; do
22c1318c5bSMichal Berger		eval "${array}[elem]=$elem"
23c1318c5bSMichal Berger	done
24c1318c5bSMichal Berger}
25c1318c5bSMichal Berger
26c1318c5bSMichal Bergerfold_array_onto_string() {
27c1318c5bSMichal Berger	local cpus=("$@")
28c1318c5bSMichal Berger
29c1318c5bSMichal Berger	local IFS=","
30c1318c5bSMichal Berger	echo "${cpus[*]}"
31c1318c5bSMichal Berger}
32c1318c5bSMichal Berger
33c1318c5bSMichal Bergerparse_cpu_list() {
34c1318c5bSMichal Berger	local list=$1
35c1318c5bSMichal Berger	local elem elems cpus
36c1318c5bSMichal Berger
37c1318c5bSMichal Berger	# 0-2,4,6-9, etc.
38c1318c5bSMichal Berger	IFS="," read -ra elems < "$list"
39c1318c5bSMichal Berger
40c1318c5bSMichal Berger	((${#elems[@]} > 0)) || return 0
41c1318c5bSMichal Berger
42c1318c5bSMichal Berger	for elem in "${elems[@]}"; do
43c1318c5bSMichal Berger		if [[ $elem == *-* ]]; then
44c1318c5bSMichal Berger			local start=${elem%-*} end=${elem#*-}
45c1318c5bSMichal Berger			while ((start <= end)); do
46c1318c5bSMichal Berger				cpus[start++]=$start
47c1318c5bSMichal Berger			done
48c1318c5bSMichal Berger		else
49c1318c5bSMichal Berger			cpus[elem]=$elem
50c1318c5bSMichal Berger		fi
51c1318c5bSMichal Berger	done
52c1318c5bSMichal Berger	printf '%u\n' "${!cpus[@]}"
53c1318c5bSMichal Berger}
54c1318c5bSMichal Berger
55c1318c5bSMichal Bergermap_cpus_node() {
56c1318c5bSMichal Berger	local node_idx=$1
57c1318c5bSMichal Berger	local -n _cpu_node_map=node_${node_idx}_cpu
58c1318c5bSMichal Berger	local cpu_idx core_idx
59c1318c5bSMichal Berger
60c1318c5bSMichal Berger	for cpu_idx in $(parse_cpu_list "$sysfs_node/node$node_idx/cpulist"); do
610a4a5235SMichal Berger		if is_cpu_online_f "$cpu_idx"; then
62c1318c5bSMichal Berger			core_idx=$(< "$sysfs_cpu/cpu$cpu_idx/topology/core_id")
63c1318c5bSMichal Berger			local -n _cpu_core_map=node_${node_idx}_core_${core_idx}
64c1318c5bSMichal Berger			_cpu_core_map+=("$cpu_idx") cpu_core_map[cpu_idx]=$core_idx
657832b34fSMichal Berger			local -n _cpu_siblings=node_${node_idx}_core_${core_idx}_thread_${cpu_idx}
667832b34fSMichal Berger			_cpu_siblings=($(parse_cpu_list "$sysfs_cpu/cpu$cpu_idx/topology/thread_siblings_list"))
677832b34fSMichal Berger			cpu_siblings[cpu_idx]="node_${node_idx}_core_${core_idx}_thread_${cpu_idx}[@]"
68c1318c5bSMichal Berger		fi
69c1e9ed6dSMichal Berger		_cpu_node_map[cpu_idx]=$cpu_idx cpu_node_map[cpu_idx]=$node_idx
70c1318c5bSMichal Berger		cpus+=("$cpu_idx")
71c1318c5bSMichal Berger	done
72c1318c5bSMichal Berger
73c1318c5bSMichal Berger	nodes[node_idx]=$node_idx
74c1318c5bSMichal Berger}
75c1318c5bSMichal Berger
76c1318c5bSMichal Bergermap_cpus() {
77c1318c5bSMichal Berger	local -g cpus=()
787832b34fSMichal Berger	local -g cpu_siblings=()
79c1318c5bSMichal Berger	local -g nodes=()
80c1318c5bSMichal Berger	local -g cpu_node_map=()
81c1318c5bSMichal Berger	local -g cpu_core_map=()
82c1318c5bSMichal Berger	local -g core_node_map=()
83c1318c5bSMichal Berger	local node
84c1318c5bSMichal Berger
85c1318c5bSMichal Berger	unset -v "${!node_@}"
86c1318c5bSMichal Berger
87c1318c5bSMichal Berger	for node in "$sysfs_node/node"+([0-9]); do
88c1318c5bSMichal Berger		map_cpus_node "${node##*node}"
89c1318c5bSMichal Berger	done
90c1318c5bSMichal Berger}
91c1318c5bSMichal Berger
92c1318c5bSMichal Bergerget_cpus() {
93c1318c5bSMichal Berger	local node=$1
94c1318c5bSMichal Berger	local core=$2
95c1318c5bSMichal Berger	local _cpus
96c1318c5bSMichal Berger
97c1318c5bSMichal Berger	if [[ -z $node ]]; then
98c1318c5bSMichal Berger		_cpus=("${cpus[@]}")
99c1318c5bSMichal Berger	elif [[ -n $node ]]; then
100c1318c5bSMichal Berger		eval "_cpus=(\${node_${node}_cpu[@]})"
101c1318c5bSMichal Berger		if [[ -n $core ]]; then
102c1318c5bSMichal Berger			eval "_cpus=(\${node_${node}_core_${core}[@]})"
103c1318c5bSMichal Berger		fi
104c1318c5bSMichal Berger	fi
105c1318c5bSMichal Berger	((${#_cpus[@]} > 0)) || return 1
106c1318c5bSMichal Berger	printf '%u\n' "${_cpus[@]}"
107c1318c5bSMichal Berger}
108c1318c5bSMichal Berger
109c1318c5bSMichal Bergerget_isolated_cpus() {
110c1318c5bSMichal Berger	[[ -e $sysfs_cpu/isolated ]] || return 0
111c1318c5bSMichal Berger	parse_cpu_list "$sysfs_cpu/isolated"
112c1318c5bSMichal Berger}
113c1318c5bSMichal Berger
114c1318c5bSMichal Bergerget_offline_cpus() {
115c1318c5bSMichal Berger	local offline
116c1318c5bSMichal Berger
117c1318c5bSMichal Berger	[[ -e $sysfs_cpu/offline ]] || return 0
118c1318c5bSMichal Berger	parse_cpu_list "$sysfs_cpu/offline"
119c1318c5bSMichal Berger}
120c1318c5bSMichal Berger
121c1318c5bSMichal Bergerget_online_cpus() {
122c1318c5bSMichal Berger	[[ -e $sysfs_cpu/online ]] || return 0
123c1318c5bSMichal Berger	parse_cpu_list "$sysfs_cpu/online"
124c1318c5bSMichal Berger}
125c1318c5bSMichal Berger
126c1318c5bSMichal Bergeris_cpu_online() {
127c1318c5bSMichal Berger	local online
128c1318c5bSMichal Berger
129c1318c5bSMichal Berger	fold_list_onto_array online $(get_online_cpus)
130c1318c5bSMichal Berger	[[ -v online[$1] ]]
131c1318c5bSMichal Berger}
132c1318c5bSMichal Berger
133c1318c5bSMichal Bergeris_cpu_offline() {
134c1318c5bSMichal Berger	! is_cpu_online "$1"
135c1318c5bSMichal Berger}
136c1318c5bSMichal Berger
1370a4a5235SMichal Bergeris_cpu_online_f() {
1380a4a5235SMichal Berger	local cpu=$1
1390a4a5235SMichal Berger
1400a4a5235SMichal Berger	if ((cpu == 0)); then
1410a4a5235SMichal Berger		# cpu0 is special as it requires proper support in the kernel to be hot pluggable.
1420a4a5235SMichal Berger		# As such, it usually does not have its own online attribute so always check the
1430a4a5235SMichal Berger		# online list instead.
1440a4a5235SMichal Berger		is_cpu_online "$cpu"
1450a4a5235SMichal Berger	else
1460a4a5235SMichal Berger		[[ -e $sysfs_cpu/cpu$cpu/online ]] || return 1
1470a4a5235SMichal Berger		(($(< "$sysfs_cpu/cpu$cpu/online") == 1))
1480a4a5235SMichal Berger	fi
1490a4a5235SMichal Berger}
1500a4a5235SMichal Berger
1510a4a5235SMichal Bergeris_cpu_offline_f() {
1520a4a5235SMichal Berger	! is_cpu_online_f "$1"
1530a4a5235SMichal Berger}
1540a4a5235SMichal Berger
1550070858eSMichal Bergeris_numa() {
1560070858eSMichal Berger	local nodes=("$sysfs_node/node"+([0-9]))
1570070858eSMichal Berger
1580070858eSMichal Berger	((${#nodes[@]} > 1))
1590070858eSMichal Berger}
1600070858eSMichal Berger
161c1318c5bSMichal Bergeronline_cpu() {
1620a4a5235SMichal Berger	is_cpu_offline_f "$1" || return 0
1630a4a5235SMichal Berger	echo 1 > "$sysfs_cpu/cpu$1/online"
164c1318c5bSMichal Berger}
165c1318c5bSMichal Berger
166c1318c5bSMichal Bergeroffline_cpu() {
1670a4a5235SMichal Berger	is_cpu_online_f "$1" || return 0
1680a4a5235SMichal Berger	echo 0 > "$sysfs_cpu/cpu$1/online"
169c1318c5bSMichal Berger}
170c1318c5bSMichal Berger
171c1318c5bSMichal Bergermask_cpus() {
1725ad2877dSMichal Berger	printf '[%s]\n' "$(fold_array_onto_string "$@")"
173c1318c5bSMichal Berger}
174c1318c5bSMichal Berger
175c1318c5bSMichal Bergerdenied_list() {
176c1318c5bSMichal Berger	local -g denied
177c1318c5bSMichal Berger
178c1318c5bSMichal Berger	fold_list_onto_array denied $(get_offline_cpus) "$@"
179c1318c5bSMichal Berger}
180c1318c5bSMichal Berger
181c1318c5bSMichal Bergerfilter_allowed_list() {
182c1318c5bSMichal Berger	local cpu
183c1318c5bSMichal Berger
184c1318c5bSMichal Berger	for cpu in "${!allowed[@]}"; do
1855ad2877dSMichal Berger		if [[ -n ${denied[cpu]} ]] || ((cpu > 127)); then
186c1318c5bSMichal Berger			unset -v "allowed[cpu]"
187c1318c5bSMichal Berger		fi
188c1318c5bSMichal Berger	done
189c1318c5bSMichal Berger}
190c1318c5bSMichal Berger
191c1318c5bSMichal Bergerallowed_list() {
192c1318c5bSMichal Berger	local max=${1:-4}
193c1318c5bSMichal Berger	local node=${2:-0}
194c1318c5bSMichal Berger	local cpu_count=${cpu_count:--1}
195c1318c5bSMichal Berger
196c1318c5bSMichal Berger	local -g allowed
197c1318c5bSMichal Berger
198c1318c5bSMichal Berger	fold_list_onto_array allowed $(get_isolated_cpus)
199c1318c5bSMichal Berger
200c1318c5bSMichal Berger	if ((cpu_count < 0 && ${#allowed[@]} > 0)); then
201c1318c5bSMichal Berger		((max += ${#allowed[@]}))
202c1318c5bSMichal Berger	fi
203c1318c5bSMichal Berger
204c1318c5bSMichal Berger	local -n node_cpu_ref=node_${node}_cpu
205c1318c5bSMichal Berger
206c1318c5bSMichal Berger	while ((${#allowed[@]} < max && ++cpu_count < ${#node_cpu_ref[@]})); do
207c1318c5bSMichal Berger		fold_list_onto_array allowed $(get_cpus "$node" "${cpu_core_map[node_cpu_ref[cpu_count]]}")
208c1318c5bSMichal Berger	done
209c1318c5bSMichal Berger
210c1318c5bSMichal Berger	filter_allowed_list
211c1318c5bSMichal Berger
212c1318c5bSMichal Berger	if ((${#allowed[@]} == max)); then
213c1318c5bSMichal Berger		return 0
214c1318c5bSMichal Berger	elif ((cpu_count == ${#node_cpu_ref[@]})); then
215c1318c5bSMichal Berger		return 0
216c1318c5bSMichal Berger	else
217c1318c5bSMichal Berger		allowed_list "$max" "$node"
218c1318c5bSMichal Berger	fi
219c1318c5bSMichal Berger}
220c1318c5bSMichal Berger
221c1318c5bSMichal Bergerget_proc_cpu_affinity() {
222c1318c5bSMichal Berger	xtrace_disable
223c1318c5bSMichal Berger
224c1318c5bSMichal Berger	local pid=${1:-$$}
2257b52e4c1SMichal Berger	local status val status_file
226c1318c5bSMichal Berger
2277b52e4c1SMichal Berger	if [[ -e $pid ]]; then
2287b52e4c1SMichal Berger		status_file=$pid
2297b52e4c1SMichal Berger	elif [[ -e /proc/$pid/status ]]; then
2307b52e4c1SMichal Berger		status_file=/proc/$pid/status
2317b52e4c1SMichal Berger	else
2327b52e4c1SMichal Berger		return 1
2337b52e4c1SMichal Berger	fi
2347b52e4c1SMichal Berger
235*7bc1134dSMichal Berger	# shellcheck disable=SC2188
236c1318c5bSMichal Berger	while IFS=":"$'\t' read -r status val; do
237c1318c5bSMichal Berger		if [[ $status == Cpus_allowed_list ]]; then
238c1318c5bSMichal Berger			parse_cpu_list <(echo "$val")
239c1318c5bSMichal Berger			return 0
240c1318c5bSMichal Berger		fi
241*7bc1134dSMichal Berger	done < <(< "$status_file")
242c1318c5bSMichal Berger
243c1318c5bSMichal Berger	xtrace_restore
244c1318c5bSMichal Berger}
245c1318c5bSMichal Berger
246c1318c5bSMichal Bergermap_cpufreq() {
247c1318c5bSMichal Berger	# This info is used to cross-reference current cpufreq setup with
248c1318c5bSMichal Berger	# what DPDK's governor actually puts in place.
249c1318c5bSMichal Berger
250c1318c5bSMichal Berger	local -g cpufreq_drivers=()
251c1318c5bSMichal Berger	local -g cpufreq_governors=()
252c1318c5bSMichal Berger	local -g cpufreq_base_freqs=()
253c1318c5bSMichal Berger	local -g cpufreq_max_freqs=()
254c1318c5bSMichal Berger	local -g cpufreq_min_freqs=()
255c1318c5bSMichal Berger	local -g cpufreq_cur_freqs=()
256c1318c5bSMichal Berger	local -g cpufreq_is_turbo=()
257c1318c5bSMichal Berger	local -g cpufreq_available_freqs=()
258c1318c5bSMichal Berger	local -g cpufreq_available_governors=()
259c1318c5bSMichal Berger	local -g cpufreq_high_prio=()
260c1318c5bSMichal Berger	local -g cpufreq_non_turbo_ratio=()
261c1318c5bSMichal Berger	local -g cpufreq_setspeed=()
262c1318c5bSMichal Berger	local -g cpuinfo_max_freqs=()
263c1318c5bSMichal Berger	local -g cpuinfo_min_freqs=()
264c1318c5bSMichal Berger	local -g turbo_enabled=0
265c1318c5bSMichal Berger	local cpu cpu_idx
266c1318c5bSMichal Berger
267c1318c5bSMichal Berger	for cpu in "$sysfs_cpu/cpu"+([0-9]); do
268c1318c5bSMichal Berger		cpu_idx=${cpu##*cpu}
269c1318c5bSMichal Berger		[[ -e $cpu/cpufreq ]] || continue
270c1318c5bSMichal Berger		cpufreq_drivers[cpu_idx]=$(< "$cpu/cpufreq/scaling_driver")
271c1318c5bSMichal Berger		cpufreq_governors[cpu_idx]=$(< "$cpu/cpufreq/scaling_governor")
272c1318c5bSMichal Berger
273c1318c5bSMichal Berger		# In case HWP is on
274c1318c5bSMichal Berger		if [[ -e $cpu/cpufreq/base_frequency ]]; then
275c1318c5bSMichal Berger			cpufreq_base_freqs[cpu_idx]=$(< "$cpu/cpufreq/base_frequency")
276c1318c5bSMichal Berger		fi
277c1318c5bSMichal Berger
278c1318c5bSMichal Berger		cpufreq_cur_freqs[cpu_idx]=$(< "$cpu/cpufreq/scaling_cur_freq")
279c1318c5bSMichal Berger		cpufreq_max_freqs[cpu_idx]=$(< "$cpu/cpufreq/scaling_max_freq")
280c1318c5bSMichal Berger		cpufreq_min_freqs[cpu_idx]=$(< "$cpu/cpufreq/scaling_min_freq")
281c1318c5bSMichal Berger
282c1318c5bSMichal Berger		local -n available_governors=available_governors_cpu_${cpu_idx}
283c1318c5bSMichal Berger		cpufreq_available_governors[cpu_idx]="available_governors_cpu_${cpu_idx}[@]"
284c1318c5bSMichal Berger		available_governors=($(< "$cpu/cpufreq/scaling_available_governors"))
285c1318c5bSMichal Berger
286c1318c5bSMichal Berger		local -n available_freqs=available_freqs_cpu_${cpu_idx}
287c1318c5bSMichal Berger		cpufreq_available_freqs[cpu_idx]="available_freqs_cpu_${cpu_idx}[@]"
288c1318c5bSMichal Berger
289c1318c5bSMichal Berger		case "${cpufreq_drivers[cpu_idx]}" in
290c1318c5bSMichal Berger			acpi-cpufreq)
291c1318c5bSMichal Berger				available_freqs=($(< "$cpu/cpufreq/scaling_available_frequencies"))
292c1318c5bSMichal Berger				if ((available_freqs[0] - 1000 == available_freqs[1])); then
293c1318c5bSMichal Berger					cpufreq_is_turbo[cpu_idx]=1
294c1318c5bSMichal Berger				else
295c1318c5bSMichal Berger					cpufreq_is_turbo[cpu_idx]=0
296c1318c5bSMichal Berger				fi
297c1318c5bSMichal Berger				cpufreq_setspeed[cpu_idx]=$(< "$cpu/cpufreq/scaling_setspeed")
298c1318c5bSMichal Berger				;;
299c1318c5bSMichal Berger			intel_pstate | intel_cpufreq) # active or passive
300c1318c5bSMichal Berger				local non_turbo_ratio base_max_freq num_freq freq is_turbo=0
301c1318c5bSMichal Berger
302c1318c5bSMichal Berger				non_turbo_ratio=$("$testdir/rdmsr.pl" "$cpu_idx" 0xce)
303c1318c5bSMichal Berger				cpuinfo_min_freqs[cpu_idx]=$(< "$cpu/cpufreq/cpuinfo_min_freq")
304c1318c5bSMichal Berger				cpuinfo_max_freqs[cpu_idx]=$(< "$cpu/cpufreq/cpuinfo_max_freq")
305c1318c5bSMichal Berger				cpufreq_non_turbo_ratio[cpu_idx]=$(((non_turbo_ratio >> 8) & 0xff))
306c1318c5bSMichal Berger				if ((cpufreq_base_freqs[cpu_idx] / 100000 > cpufreq_non_turbo_ratio[cpu_idx])); then
307c1318c5bSMichal Berger					cpufreq_high_prio[cpu_idx]=1
308c1318c5bSMichal Berger					base_max_freq=${cpufreq_base_freqs[cpu_idx]}
309c1318c5bSMichal Berger				else
310c1318c5bSMichal Berger					cpufreq_high_prio[cpu_idx]=0
311c1318c5bSMichal Berger					base_max_freq=$((cpufreq_non_turbo_ratio[cpu_idx] * 100000))
312c1318c5bSMichal Berger				fi
313c1318c5bSMichal Berger				num_freqs=$(((base_max_freq - cpuinfo_min_freqs[cpu_idx]) / 100000 + 1))
314c1318c5bSMichal Berger				if ((base_max_freq < cpuinfo_max_freqs[cpu_idx])); then
315c1318c5bSMichal Berger					((num_freqs += 1))
316c1318c5bSMichal Berger					cpufreq_is_turbo[cpu_idx]=1
317c1318c5bSMichal Berger				else
318c1318c5bSMichal Berger					cpufreq_is_turbo[cpu_idx]=0
319c1318c5bSMichal Berger				fi
320c1318c5bSMichal Berger				available_freqs=()
321c1318c5bSMichal Berger				for ((freq = 0; freq < num_freqs; freq++)); do
322c1318c5bSMichal Berger					if ((freq == 0 && cpufreq_is_turbo[cpu_idx] == 1)); then
323c1318c5bSMichal Berger						available_freqs[freq]=$((base_max_freq + 1))
324c1318c5bSMichal Berger					else
325c1318c5bSMichal Berger						available_freqs[freq]=$((base_max_freq - (freq - cpufreq_is_turbo[cpu_idx]) * 100000))
326c1318c5bSMichal Berger					fi
327c1318c5bSMichal Berger				done
328c1318c5bSMichal Berger				;;
329454561bfSRichael Zhuang			cppc_cpufreq)
330454561bfSRichael Zhuang				cpufreq_setspeed[cpu_idx]=$(< "$cpu/cpufreq/scaling_setspeed")
331454561bfSRichael Zhuang				scaling_min_freqs[cpu_idx]=$(< "$cpu/cpufreq/scaling_min_freq")
332454561bfSRichael Zhuang				scaling_max_freqs[cpu_idx]=$(< "$cpu/cpufreq/scaling_max_freq")
333454561bfSRichael Zhuang				cpuinfo_max_freqs[cpu_idx]=$(< "$cpu/cpufreq/cpuinfo_max_freq")
334454561bfSRichael Zhuang				nominal_perf[cpu_idx]=$(< "$cpu/acpi_cppc/nominal_perf")
335454561bfSRichael Zhuang				highest_perf[cpu_idx]=$(< "$cpu/acpi_cppc/highest_perf")
336454561bfSRichael Zhuang
337454561bfSRichael Zhuang				#the unit of highest_perf and nominal_perf differs on different arm platforms.
338454561bfSRichael Zhuang				#For highest_perf, it maybe 300 or 3000000, both means 3.0GHz.
339454561bfSRichael Zhuang				if ((highest_perf[cpu_idx] > nominal_perf[cpu_idx] && (\
340454561bfSRichael Zhuang					highest_perf[cpu_idx] == cpuinfo_max_freqs[cpu_idx] || \
341454561bfSRichael Zhuang					highest_perf[cpu_idx] * 10000 == cpuinfo_max_freqs[cpu_idx]))); then
342454561bfSRichael Zhuang					cpufreq_is_turbo[cpu_idx]=1
343454561bfSRichael Zhuang				else
344454561bfSRichael Zhuang					cpufreq_is_turbo[cpu_idx]=0
345454561bfSRichael Zhuang				fi
346454561bfSRichael Zhuang
347454561bfSRichael Zhuang				if ((nominal_perf[cpu_idx] < 10000)); then
348454561bfSRichael Zhuang					nominal_perf[cpu_idx]=$((nominal_perf[cpu_idx] * 10000))
349454561bfSRichael Zhuang				fi
350454561bfSRichael Zhuang
351454561bfSRichael Zhuang				num_freqs=$(((nominal_perf[cpu_idx] - scaling_min_freqs[cpu_idx]) / 100000 + 1 + \
352454561bfSRichael Zhuang					cpufreq_is_turbo[cpu_idx]))
353454561bfSRichael Zhuang
354454561bfSRichael Zhuang				available_freqs=()
355454561bfSRichael Zhuang				for ((freq = 0; freq < num_freqs; freq++)); do
356454561bfSRichael Zhuang					if ((freq == 0 && cpufreq_is_turbo[cpu_idx] == 1)); then
357454561bfSRichael Zhuang						available_freqs[freq]=$((scaling_max_freqs[cpu_idx]))
358454561bfSRichael Zhuang					else
359454561bfSRichael Zhuang						available_freqs[freq]=$((nominal_perf[cpu_idx] - (\
360454561bfSRichael Zhuang							freq - cpufreq_is_turbo[cpu_idx]) * 100000))
361454561bfSRichael Zhuang					fi
362454561bfSRichael Zhuang				done
363454561bfSRichael Zhuang				;;
364c1318c5bSMichal Berger		esac
365c1318c5bSMichal Berger	done
366c1318c5bSMichal Berger	if [[ -e $sysfs_cpu/cpufreq/boost ]]; then
367c1318c5bSMichal Berger		turbo_enabled=$(< "$sysfs_cpu/cpufreq/boost")
368c1318c5bSMichal Berger	elif [[ -e $sysfs_cpu/intel_pstate/no_turbo ]]; then
369c1318c5bSMichal Berger		turbo_enabled=$((!$(< "$sysfs_cpu/intel_pstate/no_turbo")))
370c1318c5bSMichal Berger	fi
371c1318c5bSMichal Berger}
372c1318c5bSMichal Berger
373c1318c5bSMichal Bergerset_cpufreq() {
374c1318c5bSMichal Berger	local cpu=$1
375c1318c5bSMichal Berger	local min_freq=$2
376c1318c5bSMichal Berger	local max_freq=$3
377c1318c5bSMichal Berger	local cpufreq=$sysfs_cpu/cpu$cpu/cpufreq
378c1318c5bSMichal Berger
379c1318c5bSMichal Berger	# Map the cpufreq info first
380c1318c5bSMichal Berger	[[ -n ${cpufreq_drivers[cpu]} ]] || return 1
381c1318c5bSMichal Berger	[[ -n $min_freq ]] || return 1
382c1318c5bSMichal Berger
383c1318c5bSMichal Berger	case "${cpufreq_drivers[cpu]}" in
3845971ef1dSRichael Zhuang		acpi-cpufreq | cppc_cpufreq)
385dc32e1baSRichael Zhuang			if [[ $(< "$cpufreq/scaling_governor") != userspace ]]; then
386dc32e1baSRichael Zhuang				echo "userspace" > "$cpufreq/scaling_governor"
387c1318c5bSMichal Berger			fi
388c1318c5bSMichal Berger			echo "$min_freq" > "$cpufreq/scaling_setspeed"
389c1318c5bSMichal Berger			;;
390c1318c5bSMichal Berger		intel_pstate | intel_cpufreq)
391c1318c5bSMichal Berger			if [[ -n $max_freq ]] && ((max_freq >= min_freq)); then
392c1318c5bSMichal Berger				echo "$max_freq" > "$cpufreq/scaling_max_freq"
393c1318c5bSMichal Berger			fi
3947832b34fSMichal Berger			if ((min_freq <= cpufreq_max_freqs[cpu])); then
3957832b34fSMichal Berger				echo "$min_freq" > "$cpufreq/scaling_min_freq"
3967832b34fSMichal Berger			fi
397c1318c5bSMichal Berger			;;
398c1318c5bSMichal Berger	esac
399c1318c5bSMichal Berger}
400c313f1b2SMichal Berger
4011194f6ccSMichal Bergerset_cpufreq_governor() {
4021194f6ccSMichal Berger	local cpu=$1
4031194f6ccSMichal Berger	local governor=$2
4041194f6ccSMichal Berger	local cpufreq=$sysfs_cpu/cpu$cpu/cpufreq
4051194f6ccSMichal Berger
4061194f6ccSMichal Berger	if [[ $(< "$cpufreq/scaling_governor") != "$governor" ]]; then
4071194f6ccSMichal Berger		echo "$governor" > "$cpufreq/scaling_governor"
4081194f6ccSMichal Berger	fi
4091194f6ccSMichal Berger}
4101194f6ccSMichal Berger
411c313f1b2SMichal Bergerexec_under_dynamic_scheduler() {
412d841e24bSMichal Berger	if [[ -e /proc/$spdk_pid/status ]]; then
413d841e24bSMichal Berger		killprocess "$spdk_pid"
414d841e24bSMichal Berger	fi
4158571999dSMichal Berger	"$@" --wait-for-rpc &
416c313f1b2SMichal Berger	spdk_pid=$!
417c313f1b2SMichal Berger	# Give some time for the app to init itself
418c313f1b2SMichal Berger	waitforlisten "$spdk_pid"
419c313f1b2SMichal Berger	"$rootdir/scripts/rpc.py" framework_set_scheduler dynamic
420c313f1b2SMichal Berger	"$rootdir/scripts/rpc.py" framework_start_init
421c313f1b2SMichal Berger}
422c313f1b2SMichal Berger
4234e4b993aSSeungYeon Shinexec_under_static_scheduler() {
4244e4b993aSSeungYeon Shin	if [[ -e /proc/$spdk_pid/status ]]; then
4254e4b993aSSeungYeon Shin		killprocess "$spdk_pid"
4264e4b993aSSeungYeon Shin	fi
4274e4b993aSSeungYeon Shin	"$@" --wait-for-rpc &
4284e4b993aSSeungYeon Shin	spdk_pid=$!
4294e4b993aSSeungYeon Shin	# Give some time for the app to init itself
4304e4b993aSSeungYeon Shin	waitforlisten "$spdk_pid"
4314e4b993aSSeungYeon Shin}
4324e4b993aSSeungYeon Shin
43319377d66STomasz Zawadzki# Gather busy/idle stats since this function was last called
43419377d66STomasz Zawadzkiget_thread_stats_current() {
43519377d66STomasz Zawadzki	xtrace_disable
43619377d66STomasz Zawadzki
43719377d66STomasz Zawadzki	local total_busy total_idle
43819377d66STomasz Zawadzki
43919377d66STomasz Zawadzki	_get_thread_stats total_busy total_idle
44019377d66STomasz Zawadzki
44119377d66STomasz Zawadzki	for thread in "${!thread_map[@]}"; do
44219377d66STomasz Zawadzki		: $((busy[thread] = total_busy[thread] - past_busy[thread], past_busy[thread] = total_busy[thread]))
44319377d66STomasz Zawadzki		: $((idle[thread] = total_idle[thread] - past_idle[thread], past_idle[thread] = total_idle[thread]))
44419377d66STomasz Zawadzki	done
44519377d66STomasz Zawadzki	xtrace_restore
44619377d66STomasz Zawadzki}
44719377d66STomasz Zawadzki
44819377d66STomasz Zawadzki# Gather busy/idle stats since application start
449c313f1b2SMichal Bergerget_thread_stats() {
450c313f1b2SMichal Berger	xtrace_disable
451c313f1b2SMichal Berger	_get_thread_stats busy idle
452c313f1b2SMichal Berger	xtrace_restore
453c313f1b2SMichal Berger}
454c313f1b2SMichal Berger
455c313f1b2SMichal Berger_get_thread_stats() {
456c313f1b2SMichal Berger	local list_busy=$1
457c313f1b2SMichal Berger	local list_idle=$2
458c313f1b2SMichal Berger	local thread threads stats
459c313f1b2SMichal Berger
460c313f1b2SMichal Berger	stats=$(rpc_cmd thread_get_stats | jq -r '.threads[]')
461c313f1b2SMichal Berger	threads=($(jq -r '.id' <<< "$stats"))
462c313f1b2SMichal Berger
463c313f1b2SMichal Berger	for thread in "${threads[@]}"; do
464c313f1b2SMichal Berger		eval "${list_busy}[$thread]=\$(jq -r \"select(.id == $thread) | .busy\" <<< \$stats)"
465c313f1b2SMichal Berger		eval "${list_idle}[$thread]=\$(jq -r \"select(.id == $thread) | .idle\" <<< \$stats)"
466c313f1b2SMichal Berger		thread_map[thread]=$(jq -r "select(.id == $thread) | .name" <<< "$stats")
467c313f1b2SMichal Berger	done
468c313f1b2SMichal Berger}
469049d587eSMichal Berger
470049d587eSMichal Bergerget_cpu_stat() {
471049d587eSMichal Berger	local cpu_idx=$1
4720ea89560SMichal Berger	local stat=$2 stats astats
473049d587eSMichal Berger
47459c811f0SMichal Berger	# cpu0 0 0 0 0 0 0 0 0 0 -> _cpu0=(0 0 0 0 0 0 0 0 0)
47559c811f0SMichal Berger	source <(grep '^cpu[0-9]' /proc/stat | sed 's/\([^ ]*\) \(.*\)/_\1=(\2)/')
47659c811f0SMichal Berger
47759c811f0SMichal Berger	# If we were called with valid cpu id return requested time
47859c811f0SMichal Berger	[[ -v _cpu$cpu_idx ]] || return 0
47959c811f0SMichal Berger	local -n cpu_stat=_cpu$cpu_idx
480049d587eSMichal Berger
481049d587eSMichal Berger	case "$stat" in
48259c811f0SMichal Berger		idle) echo "${cpu_stat[3]}" ;;
48359c811f0SMichal Berger		*) printf '%u\n' "${cpu_stat[@]}" ;;
484049d587eSMichal Berger	esac
485049d587eSMichal Berger}
486049d587eSMichal Berger
487049d587eSMichal Bergercreate_thread() {
4887347f60bSMichal Berger	rpc_cmd --plugin "$plugin" scheduler_thread_create "$@"
489049d587eSMichal Berger}
490049d587eSMichal Berger
491049d587eSMichal Bergerdestroy_thread() {
4927347f60bSMichal Berger	rpc_cmd --plugin "$plugin" scheduler_thread_delete "$@"
493049d587eSMichal Berger}
494049d587eSMichal Berger
495049d587eSMichal Bergeractive_thread() {
4967347f60bSMichal Berger	rpc_cmd --plugin "$plugin" scheduler_thread_set_active "$@"
497049d587eSMichal Berger}
4980ea89560SMichal Berger
4990ea89560SMichal Bergerget_cpu_time() {
5000ea89560SMichal Berger	xtrace_disable
5010ea89560SMichal Berger
50236eea926SMichal Berger	local interval=$1 cpu_time=${2:-idle} print=${3:-0} wait=${4:-1} interval_count
50336eea926SMichal Berger	shift 4
5040ea89560SMichal Berger	local cpus=("$@") cpu
5050ea89560SMichal Berger	local stats stat old_stats avg_load
5060ea89560SMichal Berger	local total_sample
50759c811f0SMichal Berger	local keep_going=0
5080ea89560SMichal Berger
5090ea89560SMichal Berger	# Exposed for the caller
5100ea89560SMichal Berger	local -g cpu_times=()
5110ea89560SMichal Berger	local -g avg_cpu_time=()
5120ea89560SMichal Berger
5130ea89560SMichal Berger	# cpu_time:
5140ea89560SMichal Berger	# 0 - user (time spent in user mode)
5150ea89560SMichal Berger	# 1 - nice (Time spent in user mode with low priority)
5160ea89560SMichal Berger	# 2 - system (Time spent in system mode)
5170ea89560SMichal Berger	# 3 - idle (Time spent in the idle task)
5180ea89560SMichal Berger	# 4 - iowait (Time waiting for I/O to complete)
5190ea89560SMichal Berger	# 5 - irq (Time servicing interrupts)
5200ea89560SMichal Berger	# 6 - softirq (Time servicing softirqs)
5210ea89560SMichal Berger	# 7 - steal (Stolen time)
5220ea89560SMichal Berger	# 8 - guest (Time spent running a virtual CPU)
5230ea89560SMichal Berger	# 9 - guest_nice (Time spent running a niced guest)
5240ea89560SMichal Berger
5259b893966SMichal Berger	local -gA cpu_time_map
5260ea89560SMichal Berger	cpu_time_map["user"]=0
5270ea89560SMichal Berger	cpu_time_map["nice"]=1
5280ea89560SMichal Berger	cpu_time_map["system"]=2
5290ea89560SMichal Berger	cpu_time_map["idle"]=3
5300ea89560SMichal Berger	cpu_time_map["iowait"]=4
5310ea89560SMichal Berger	cpu_time_map["irq"]=5
5320ea89560SMichal Berger	cpu_time_map["softirq"]=6
5330ea89560SMichal Berger	cpu_time_map["steal"]=7
5340ea89560SMichal Berger	cpu_time_map["guest"]=8
5350ea89560SMichal Berger	cpu_time_map["guest_nice"]=9
5360ea89560SMichal Berger
5370ea89560SMichal Berger	# Clear up the env
5380ea89560SMichal Berger	unset -v ${!stat_@}
5390ea89560SMichal Berger	unset -v ${!old_stat_@}
5400ea89560SMichal Berger	unset -v ${!avg_stat@}
5410ea89560SMichal Berger	unset -v ${!avg_load@}
5429b893966SMichal Berger	unset -v ${!raw_samples@}
5430ea89560SMichal Berger
5447d080339SMichal Berger	cpu_time=${cpu_time_map["$cpu_time"]}
54559c811f0SMichal Berger	interval_count=0
54659c811f0SMichal Berger	if ((interval <= 0)); then
54759c811f0SMichal Berger		keep_going=1
54859c811f0SMichal Berger	else
549c9c7c281SJosh Soref		# We skip first sample to have min 2 for stat comparison
55059c811f0SMichal Berger		interval=$((interval + 1))
55159c811f0SMichal Berger	fi
55259c811f0SMichal Berger	while ((interval_count++, keep_going ? 1 : --interval >= 0)); do
55359c811f0SMichal Berger		((interval_count > 1 && print == 1)) && print_cpu_time_header
55459c811f0SMichal Berger		get_cpu_stat all
5550ea89560SMichal Berger		for cpu in "${cpus[@]}"; do
5560ea89560SMichal Berger			local -n old_stats=old_stats_$cpu
5570ea89560SMichal Berger			local -n avg_load=avg_load_$cpu
5589b893966SMichal Berger			local -n raw_samples=raw_samples_$cpu
55959c811f0SMichal Berger			local -n stats=_cpu$cpu
5600ea89560SMichal Berger			sample_stats=() total_sample=0
5610ea89560SMichal Berger
5620ea89560SMichal Berger			if ((interval_count == 1)); then
5630ea89560SMichal Berger				# Skip first sample
5640ea89560SMichal Berger				old_stats=("${stats[@]}")
5650ea89560SMichal Berger				continue
5660ea89560SMichal Berger			fi
5670ea89560SMichal Berger			for stat in "${!stats[@]}"; do
5680ea89560SMichal Berger				avg_load[stat]="stat_${stat}_${cpu}[@]"
5690ea89560SMichal Berger				sample_stats[stat]=$((stats[stat] - old_stats[stat]))
5700ea89560SMichal Berger				: $((total_sample += sample_stats[stat]))
5710ea89560SMichal Berger			done
5720ea89560SMichal Berger			for stat in "${!stats[@]}"; do
5730ea89560SMichal Berger				local -n avg_stat=stat_${stat}_${cpu}
5749b893966SMichal Berger				local -n raw_samples_ref=raw_samples_${stat}_${cpu}
5759b893966SMichal Berger				raw_samples[stat]="raw_samples_${stat}_${cpu}[@]"
5769b893966SMichal Berger				raw_samples_ref+=("${stats[stat]}")
5770ea89560SMichal Berger				avg_stat+=($((sample_stats[stat] * 100 / (total_sample == 0 ? 1 : total_sample))))
5780ea89560SMichal Berger			done
5790ea89560SMichal Berger			old_stats=("${stats[@]}")
58059c811f0SMichal Berger			((print == 1)) && print_cpu_time "$cpu"
5810ea89560SMichal Berger		done
58236eea926SMichal Berger		sleep "${wait}s"
5830ea89560SMichal Berger	done
5840ea89560SMichal Berger
5850ea89560SMichal Berger	# We collected % for each time. Now determine the avg % for requested time.
5860ea89560SMichal Berger	local load stat_load
5870ea89560SMichal Berger	for cpu in "${cpus[@]}"; do
5880ea89560SMichal Berger		load=0
5890ea89560SMichal Berger		local -n avg_load_cpu=avg_load_$cpu
5900ea89560SMichal Berger		stat_load=("${!avg_load_cpu[cpu_time]}")
5910ea89560SMichal Berger		for stat in "${stat_load[@]}"; do
5920ea89560SMichal Berger			: $((load += stat))
5930ea89560SMichal Berger		done
5940ea89560SMichal Berger		cpu_times[cpu]=${stat_load[*]}
5950ea89560SMichal Berger		avg_cpu_time[cpu]=$((load / ${#stat_load[@]}))
5960ea89560SMichal Berger	done
5970ea89560SMichal Berger
5980ea89560SMichal Berger	xtrace_restore
5990ea89560SMichal Berger}
600330e9f77SMichal Berger
60159c811f0SMichal Bergerprint_cpu_time_header() {
60259c811f0SMichal Berger	local ts
60399bebaceSMichal Berger	ts=$(date "+%R:%S %Z")
60459c811f0SMichal Berger
6058d588fbcSMichal Berger	printf '(%s) %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s (test:%s)\n' \
60659c811f0SMichal Berger		"$ts" \
60759c811f0SMichal Berger		"CPU" "%usr" "%nice" "%sys" "%iowait" "%irq" "%soft" "%steal" \
6088d588fbcSMichal Berger		"%guest" "%gnice" "%idle" "${TEST_TAG:-N/A}"
60959c811f0SMichal Berger}
61059c811f0SMichal Berger
61159c811f0SMichal Bergerprint_cpu_time() {
61259c811f0SMichal Berger	local cpu=$1
61359c811f0SMichal Berger
61459c811f0SMichal Berger	local -n _cpu_ref=avg_load_$cpu
61559c811f0SMichal Berger	((${#_cpu_ref[@]} > 0)) || return 0
61659c811f0SMichal Berger
61759c811f0SMichal Berger	usr=("${!_cpu_ref[0]}")
61859c811f0SMichal Berger	nice=("${!_cpu_ref[1]}")
61959c811f0SMichal Berger	system=("${!_cpu_ref[2]}")
62059c811f0SMichal Berger	idle=("${!_cpu_ref[3]}")
62159c811f0SMichal Berger	iowait=("${!_cpu_ref[4]}")
62259c811f0SMichal Berger	irq=("${!_cpu_ref[5]}")
62359c811f0SMichal Berger	soft=("${!_cpu_ref[6]}")
62459c811f0SMichal Berger	steal=("${!_cpu_ref[7]}")
62559c811f0SMichal Berger	guest=("${!_cpu_ref[8]}")
62659c811f0SMichal Berger	gnice=("${!_cpu_ref[9]}")
62759c811f0SMichal Berger
62899bebaceSMichal Berger	printf '%23u %8u %8u %8u %8u %8u %8u %8u %8u %8u %8u\n' \
62959c811f0SMichal Berger		"$cpu" \
63059c811f0SMichal Berger		"${usr[-1]}" \
63159c811f0SMichal Berger		"${nice[-1]}" \
63259c811f0SMichal Berger		"${system[-1]}" \
63359c811f0SMichal Berger		"${iowait[-1]}" \
63459c811f0SMichal Berger		"${irq[-1]}" \
63559c811f0SMichal Berger		"${soft[-1]}" \
63659c811f0SMichal Berger		"${steal[-1]}" \
63759c811f0SMichal Berger		"${guest[-1]}" \
63859c811f0SMichal Berger		"${gnice[-1]}" \
63959c811f0SMichal Berger		"${idle[-1]}"
64059c811f0SMichal Berger}
64159c811f0SMichal Berger
642330e9f77SMichal Bergercollect_cpu_idle() {
643330e9f77SMichal Berger	((${#cpus_to_collect[@]} > 0)) || return 1
644330e9f77SMichal Berger
645330e9f77SMichal Berger	local time=${1:-5}
646330e9f77SMichal Berger	local cpu
647330e9f77SMichal Berger	local samples
648330e9f77SMichal Berger	local -g is_idle=()
649330e9f77SMichal Berger
650330e9f77SMichal Berger	printf 'Collecting cpu idle stats (cpus: %s) for %u seconds...\n' \
651330e9f77SMichal Berger		"${cpus_to_collect[*]}" "$time"
652330e9f77SMichal Berger
65336eea926SMichal Berger	get_cpu_time "$time" idle 0 1 "${cpus_to_collect[@]}"
654330e9f77SMichal Berger
6557b52e4c1SMichal Berger	local user_load load_median user_spdk_load
656330e9f77SMichal Berger	for cpu in "${cpus_to_collect[@]}"; do
657330e9f77SMichal Berger		samples=(${cpu_times[cpu]})
6581dc06520SMichal Berger		load_median=$(calc_median "${samples[@]}")
6591dc06520SMichal Berger		printf '* cpu%u idle samples: %s (avg: %u%%, median: %u%%)\n' \
6601dc06520SMichal Berger			"$cpu" "${samples[*]}" "${avg_cpu_time[cpu]}" "$load_median"
661330e9f77SMichal Berger		# Cores with polling reactors have 0% idle time,
662330e9f77SMichal Berger		# while the ones in interrupt mode won't have 100% idle.
6639b893966SMichal Berger		# During the tests, polling reactors spend the major portion
6649b893966SMichal Berger		# of their cpu time in user mode. With that in mind, if the
6659b893966SMichal Berger		# general check for cpus's idleness fails, check what portion
6669b893966SMichal Berger		# of the cpu load falls into user mode. For the idle check
6679b893966SMichal Berger		# use the last sample. For the cpu load, compare user's raw
6689b893966SMichal Berger		# samples in SC_CLK_TCK context for a more detailed view.
6699b893966SMichal Berger		user_load=$(cpu_usage_clk_tck "$cpu" user)
670330e9f77SMichal Berger		if ((samples[-1] >= 70)); then
671330e9f77SMichal Berger			printf '* cpu%u is idle\n' "$cpu"
672330e9f77SMichal Berger			is_idle[cpu]=1
6739b893966SMichal Berger		elif ((user_load <= 15)); then
6749b893966SMichal Berger			printf '* cpu%u not fully idle, but user load is low so passing\n' "$cpu"
6759b893966SMichal Berger			is_idle[cpu]=1
676330e9f77SMichal Berger		else
677330e9f77SMichal Berger			printf '* cpu%u is not idle\n' "$cpu"
678330e9f77SMichal Berger			is_idle[cpu]=0
6797b52e4c1SMichal Berger			# HACK: Since we verify this in context of business of particular SPDK threads, make
6807b52e4c1SMichal Berger			# the last check against their {u,s}time to determine if we are really busy or not. This
6817b52e4c1SMichal Berger			# is meant to null and void potential jitter on the cpu.
6827b52e4c1SMichal Berger			# See https://github.com/spdk/spdk/issues/3362.
6837b52e4c1SMichal Berger			user_spdk_load=$(get_spdk_proc_time "$time" "$cpu")
6847b52e4c1SMichal Berger			if ((user_spdk_load <= 15)); then
6857b52e4c1SMichal Berger				printf '* SPDK thread pinned to cpu%u seems to be idle regardless (%u%%)\n' \
6867b52e4c1SMichal Berger					"$cpu" \
6877b52e4c1SMichal Berger					"$user_spdk_load"
6887b52e4c1SMichal Berger				is_idle[cpu]=1
6897b52e4c1SMichal Berger			fi
690330e9f77SMichal Berger		fi
691330e9f77SMichal Berger	done
692330e9f77SMichal Berger}
693d841e24bSMichal Berger
6949b893966SMichal Bergercpu_usage_clk_tck() {
6959b893966SMichal Berger	local cpu=$1 time=${2:-all}
6969b893966SMichal Berger	local user nice system usage clk_delta
6979b893966SMichal Berger
6989b893966SMichal Berger	# We should be called in get_cpu_time()'s environment.
6999b893966SMichal Berger	[[ -v raw_samples_$cpu ]] || return 1
7009b893966SMichal Berger
7019b893966SMichal Berger	local -n raw_samples=raw_samples_$cpu
7029b893966SMichal Berger	user=("${!raw_samples[cpu_time_map["user"]]}")
7039b893966SMichal Berger	nice=("${!raw_samples[cpu_time_map["nice"]]}")
7049b893966SMichal Berger	system=("${!raw_samples[cpu_time_map["system"]]}")
7059b893966SMichal Berger
7069b893966SMichal Berger	# Construct delta based on last two samples of a given time.
7079b893966SMichal Berger	case "$time" in
708b24df7cfSMichal Berger		user | all) : $((clk_delta += (user[-1] - user[-2]))) ;;&
709b24df7cfSMichal Berger		nice | all) : $((clk_delta += (nice[-1] - nice[-2]))) ;;&
710b24df7cfSMichal Berger		system | all) : $((clk_delta += (system[-1] - system[-2]))) ;;
7119b893966SMichal Berger		*) ;;
7129b893966SMichal Berger	esac
7139b893966SMichal Berger	# We assume 1s between each sample. See get_cpu_time().
7149b893966SMichal Berger	usage=$((100 * clk_delta / $(getconf CLK_TCK)))
7159b893966SMichal Berger	usage=$((usage > 100 ? 100 : usage))
7169b893966SMichal Berger
7179b893966SMichal Berger	printf '%u' "$usage"
7189b893966SMichal Berger	printf '* cpu%u %s usage: %u\n' "$cpu" "$time" "$usage" >&2
7199b893966SMichal Berger	printf '* cpu%u user samples: %s\n' "$cpu" "${user[*]}" >&2
7209b893966SMichal Berger	printf '* cpu%u nice samples: %s\n' "$cpu" "${nice[*]}" >&2
7219b893966SMichal Berger	printf '* cpu%u system samples: %s\n' "$cpu" "${system[*]}" >&2
7229b893966SMichal Berger}
7239b893966SMichal Berger
724d841e24bSMichal Bergerupdate_thread_cpus_map() {
725d841e24bSMichal Berger	local cpu
726d841e24bSMichal Berger	local -g thread_cpus=()
727d841e24bSMichal Berger	local reactor_framework
728d841e24bSMichal Berger
729d841e24bSMichal Berger	((${#cpus[@]} > 0)) || return 1
730d841e24bSMichal Berger
731d841e24bSMichal Berger	get_thread_stats
732d841e24bSMichal Berger
733d841e24bSMichal Berger	reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
734d841e24bSMichal Berger	for cpu in "${cpus[@]}"; do
735d841e24bSMichal Berger		for thread in $(jq -r "select(.lcore == $cpu) | .lw_threads[].id" <<< "$reactor_framework"); do
736d841e24bSMichal Berger			printf '* Thread %u (%s) on cpu%u\n' "$thread" "${thread_map[thread]}" "$cpu"
737d841e24bSMichal Berger			thread_cpus[thread]=$cpu
738d841e24bSMichal Berger		done
739d841e24bSMichal Berger	done
740d841e24bSMichal Berger	((${#thread_cpus[@]} > 0))
741d841e24bSMichal Berger}
7421dc06520SMichal Berger
7431dc06520SMichal Bergercalc_median() {
7441dc06520SMichal Berger	local samples=("$@") samples_sorted
7451dc06520SMichal Berger	local middle median sample
7461dc06520SMichal Berger
7471dc06520SMichal Berger	samples_sorted=($(printf '%s\n' "${samples[@]}" | sort -n))
7481dc06520SMichal Berger
7491dc06520SMichal Berger	middle=$((${#samples_sorted[@]} / 2))
7501dc06520SMichal Berger	if ((${#samples_sorted[@]} % 2 == 0)); then
7511dc06520SMichal Berger		median=$(((samples_sorted[middle - 1] + samples_sorted[middle]) / 2))
7521dc06520SMichal Berger	else
7531dc06520SMichal Berger		median=${samples_sorted[middle]}
7541dc06520SMichal Berger	fi
7551dc06520SMichal Berger
7561dc06520SMichal Berger	echo "$median"
7571dc06520SMichal Berger
7581dc06520SMichal Berger}
7597b52e4c1SMichal Berger
7607b52e4c1SMichal Bergerget_spdk_proc_time() {
7617b52e4c1SMichal Berger	# Similar to cpu_usage_clk_tck() but the values we are working here, per process, are already
7627b52e4c1SMichal Berger	# divided by SC_CLK_TCK. See proc(5).
7637b52e4c1SMichal Berger
7647b52e4c1SMichal Berger	xtrace_disable
7657b52e4c1SMichal Berger
7667b52e4c1SMichal Berger	local interval=$1 cpu=$2
7677b52e4c1SMichal Berger	local thread thread_to_time stats
7687b52e4c1SMichal Berger	local _time time _stime stime _utime utime
7690b65bb47SMichal Berger	local thread_cpu_list
7707b52e4c1SMichal Berger
7717b52e4c1SMichal Berger	[[ -e /proc/$spdk_pid/status ]] || return 1
7727b52e4c1SMichal Berger
7737b52e4c1SMichal Berger	# Find SPDK thread pinned to given cpu
7747b52e4c1SMichal Berger	for thread in "/proc/$spdk_pid/task/"*; do
7750b65bb47SMichal Berger		thread_cpu_list=($(get_proc_cpu_affinity "$thread/status"))
7760b65bb47SMichal Berger		# we aim at reactor threads and these should be bound to a single cpu
7770b65bb47SMichal Berger		((${#thread_cpu_list[@]} > 1)) && continue
7780b65bb47SMichal Berger		((thread_cpu_list[0] == cpu)) && thread_to_time=$thread && break
7797b52e4c1SMichal Berger	done
7807b52e4c1SMichal Berger
7817b52e4c1SMichal Berger	[[ -e $thread_to_time/stat ]] || return 1
7827b52e4c1SMichal Berger	interval=$((interval <= 1 ? 2 : interval))
7837b52e4c1SMichal Berger
7847b52e4c1SMichal Berger	while ((--interval >= 0)); do
7857b52e4c1SMichal Berger		# See cgroups.sh -> id_proc()
7867b52e4c1SMichal Berger		stats=$(< "$thread_to_time/stat") stats=(${stats/*) /})
7877b52e4c1SMichal Berger		_utime[interval]=${stats[11]} # Amount of time spent in user mode
7887b52e4c1SMichal Berger		_stime[interval]=${stats[12]} # Amount of time spent in kernel mode
7897b52e4c1SMichal Berger		_time[interval]=$((_utime[interval] + _stime[interval]))
7907b52e4c1SMichal Berger		((${#_time[@]} == 1)) && continue
7917b52e4c1SMichal Berger		utime+=($((_utime[interval] - _utime[interval + 1])))
7927b52e4c1SMichal Berger		stime+=($((_stime[interval] - _stime[interval + 1])))
7937b52e4c1SMichal Berger		time+=($((_time[interval] - _time[interval + 1])))
7947b52e4c1SMichal Berger		sleep 1
7957b52e4c1SMichal Berger	done
7967b52e4c1SMichal Berger
7977b52e4c1SMichal Berger	echo "stime samples: ${stime[*]}" >&2
7987b52e4c1SMichal Berger	echo "utime samples: ${utime[*]}" >&2
7997b52e4c1SMichal Berger
8007b52e4c1SMichal Berger	calc_median "${time[@]}"
8017b52e4c1SMichal Berger
8027b52e4c1SMichal Berger	xtrace_restore
8037b52e4c1SMichal Berger}
804