xref: /spdk/test/scheduler/governor.sh (revision b3bec07939ebe2ea2e0c43931705d32aa9e06719)
1#!/usr/bin/env bash
2#  SPDX-License-Identifier: BSD-3-Clause
3#  Copyright (C) 2020 Intel Corporation
4#  All rights reserved.
5#
6testdir=$(readlink -f "$(dirname "$0")")
7rootdir=$(readlink -f "$testdir/../../")
8
9source "$rootdir/test/common/autotest_common.sh"
10source "$testdir/common.sh"
11
12trap 'killprocess "$spdk_pid" || :; restore_cpufreq' EXIT
13
14restore_cpufreq() {
15	local cpu
16
17	for cpu in "$spdk_main_core" "${cpus[@]}"; do
18		set_cpufreq "$cpu" "$main_core_min_freq" "$main_core_max_freq"
19		set_cpufreq_governor "$cpu" "$initial_main_core_governor"
20	done
21}
22
23update_main_core_cpufreq() {
24	map_cpufreq
25
26	main_core_driver=${cpufreq_drivers[spdk_main_core]}
27	main_core_governor=${cpufreq_governors[spdk_main_core]}
28	main_core_set_min_freq=${cpufreq_min_freqs[spdk_main_core]}
29	main_core_set_cur_freq=${cpufreq_cur_freqs[spdk_main_core]}
30	main_core_set_max_freq=${cpufreq_max_freqs[spdk_main_core]}
31
32	if ((${#main_core_freqs[@]} == 0)); then
33		main_core_freqs=("${!cpufreq_available_freqs[spdk_main_core]}")
34		main_core_max_freq=${main_core_freqs[0]}
35		main_core_min_freq=${main_core_freqs[-1]}
36	fi
37	if ((${#main_core_freqs_map[@]} == 0)); then
38		fold_list_onto_array main_core_freqs_map "${main_core_freqs[@]}"
39	fi
40
41	case "$main_core_driver" in
42		acpi-cpufreq) main_core_setspeed=${cpufreq_setspeed[spdk_main_core]} ;;
43		intel_pstate | intel_cpufreq) main_core_setspeed=$main_core_set_max_freq ;;
44		cppc_cpufreq) main_core_setspeed=${cpufreq_setspeed[spdk_main_core]} ;;
45	esac
46
47	local thread
48	for thread in "${!cpu_siblings[spdk_main_core]}"; do
49		((thread == spdk_main_core)) && continue # handled by DPDK/scheduler
50		# While assigning cpus to SPDK, we took every thread from given core,
51		# hence the cpufreq governor should already be properly set by the
52		# DPDK. So the only thing we need to take care of is {max,min} freq.
53		# The expectation here is to see actual freq drop on the main cpu in
54		# next iterations. Both, max and min, should be set to the same value.
55		set_cpufreq "$thread" "$main_core_set_min_freq" "$main_core_set_max_freq"
56	done
57}
58
59verify_dpdk_governor() {
60	xtrace_disable
61
62	map_cpus
63	# Run the app and see if DPDK's PM subsystem is doing the proper thing. Aim at the main core.
64	# What we expect to happen is based mainly on what cpufreq driver is in use. The main assumption
65	# is that with all SPDK threads being idle the governor should start lowering the frequency for
66	# the main core.
67	#  - acpi-cpufreq:
68	#    - governor set to userspace
69	#    - lowering setspeed for the main core
70	#    - having setspeed at lowest supported frequency
71	#  - intel_pstate (active or passive)
72	#    - governor set to performance
73	#    - lowering max_freq and min_freq for the main core
74	#    - having max_freq and min_freq at lowest supported frequency
75	#  - cppc_cpufreq:
76	#    - governor set to userspace
77	#    - lowering setspeed for the main core
78	#    - having setspeed at lowest supported frequency
79
80	local -g cpus
81
82	local dir_map
83	dir_map[0]="<"
84	dir_map[1]=">"
85	dir_map[2]="=="
86
87	fold_list_onto_array cpus $(parse_cpu_list <(echo "$spdk_cpus_csv"))
88	# Get rid of the main core
89	unset -v "cpus[spdk_main_core]"
90
91	local samples=0 all_set=0 dir=-1 old_main_core_setspeed=0
92	local old_main_core_set_cur_freq=0 first_main_core_set_cur_freq=0
93
94	exec_under_dynamic_scheduler "${SPDK_APP[@]}" -m "$spdk_cpumask" --main-core "$spdk_main_core"
95
96	while ((all_set == 0 && samples++ <= 50)); do
97		update_main_core_cpufreq
98
99		if [[ $main_core_setspeed == "<unsupported>" ]]; then
100			# governor hasn't taken over yet, skip this sample
101			printf 'Waiting for DPDK governor to take over...\n'
102			continue
103		fi
104
105		if ((main_core_setspeed > old_main_core_setspeed)); then
106			dir=1
107		elif ((main_core_setspeed < old_main_core_setspeed)); then
108			dir=0
109		elif ((main_core_setspeed == old_main_core_setspeed)); then
110			# Frequency didn't change, wait for a bit, but then fall to the main check to
111			# see if cur freq actually changed or not.
112			sleep 0.5s
113		fi
114
115		if ((first_main_core_set_cur_freq == 0)); then
116			first_main_core_set_cur_freq=$main_core_set_cur_freq
117		fi
118
119		case "$main_core_driver" in
120			acpi-cpufreq | cppc_cpufreq)
121				[[ $main_core_governor == userspace ]] \
122					&& [[ -n ${main_core_freqs_map[main_core_setspeed]} ]] \
123					&& ((main_core_setspeed == main_core_freqs[-1])) \
124					&& ((dir == 0))
125				;;
126			intel_pstate | intel_cpufreq)
127				[[ $main_core_governor == performance ]] \
128					&& [[ -n ${main_core_freqs_map[main_core_setspeed]} ]] \
129					&& ((main_core_setspeed == main_core_freqs[-1])) \
130					&& ((main_core_set_max_freq == main_core_set_min_freq)) \
131					&& ((dir == 0))
132				;;
133		esac && ((main_core_set_cur_freq < old_main_core_set_cur_freq)) && all_set=1
134
135		# Print stats after first sane sample was taken
136		if ((old_main_core_setspeed != 0 && dir != -1)); then
137			printf 'MAIN DPDK cpu%u current frequency at %u KHz (%u-%u KHz), set frequency %u KHz %s %u KHz\n' \
138				"$spdk_main_core" "$main_core_set_cur_freq" "$main_core_min_freq" "$main_core_max_freq" \
139				"$main_core_setspeed" "${dir_map[dir]}" "$old_main_core_setspeed"
140		else
141			printf 'Waiting for samples...\n'
142		fi
143
144		old_main_core_setspeed=$main_core_setspeed
145		old_main_core_set_cur_freq=$main_core_set_cur_freq
146	done
147
148	((all_set == 1))
149
150	printf 'Main cpu%u frequency dropped by %u%%\n' \
151		"$spdk_main_core" \
152		$(((first_main_core_set_cur_freq - main_core_set_cur_freq) * 100 / (first_main_core_set_cur_freq - main_core_min_freq)))
153
154	xtrace_restore
155}
156
157map_cpufreq
158# Save initial scaling governor to restore it later on
159initial_main_core_governor=${cpufreq_governors[spdk_main_core]}
160
161verify_dpdk_governor
162