xref: /spdk/test/scheduler/load_balancing.sh (revision eb53c23236cccb6b698b7ca70ee783da1c574b5f)
1766dfaacSMichal Berger#!/usr/bin/env bash
2*eb53c232Spaul luse#  SPDX-License-Identifier: BSD-3-Clause
3*eb53c232Spaul luse#  Copyright (C) 2021 Intel Corporation
4*eb53c232Spaul luse#  All rights reserved.
5*eb53c232Spaul luse#
6766dfaacSMichal Bergertestdir=$(readlink -f "$(dirname "$0")")
7766dfaacSMichal Bergerrootdir=$(readlink -f "$testdir/../../")
8766dfaacSMichal Berger
9766dfaacSMichal Bergersource "$rootdir/test/common/autotest_common.sh"
10766dfaacSMichal Bergersource "$testdir/common.sh"
11766dfaacSMichal Berger
12766dfaacSMichal Bergertrap 'killprocess "$spdk_pid"' EXIT
13766dfaacSMichal Berger
14766dfaacSMichal Bergerfold_list_onto_array cpus $(parse_cpu_list <(echo "$spdk_cpus_csv"))
15766dfaacSMichal Berger# Normalize the indexes
16766dfaacSMichal Bergercpus=("${cpus[@]}")
17766dfaacSMichal Berger
18766dfaacSMichal Bergerbusy() {
19766dfaacSMichal Berger	local selected_cpus cpu
20766dfaacSMichal Berger	local reactor_framework
21766dfaacSMichal Berger	local threads thread
2284fa73ebSTomasz Zawadzki	local sched_period=1 # default, 1s
23766dfaacSMichal Berger
24766dfaacSMichal Berger	# Create two busy threads with two cpus (not including main cpu) and check if either of
25766dfaacSMichal Berger	# them is moved to either of the selected cpus. Expected load is ~100% on each thread and
26766dfaacSMichal Berger	# each thread should remain on its designated cpu.
27766dfaacSMichal Berger
28766dfaacSMichal Berger	fold_list_onto_array selected_cpus "${cpus[@]:1:2}"
29766dfaacSMichal Berger
30d47b0d2dSTomasz Zawadzki	thread0=$(create_thread -n "thread0" -m "$(mask_cpus "${selected_cpus[@]}")" -a 100)
31d47b0d2dSTomasz Zawadzki	thread1=$(create_thread -n "thread1" -m "$(mask_cpus "${selected_cpus[@]}")" -a 100)
32766dfaacSMichal Berger
3384fa73ebSTomasz Zawadzki	sleep $((10 * sched_period))
3484fa73ebSTomasz Zawadzki
35766dfaacSMichal Berger	local samples=0
36766dfaacSMichal Berger
37766dfaacSMichal Berger	xtrace_disable
38766dfaacSMichal Berger	while ((samples++ < 5)); do
3984fa73ebSTomasz Zawadzki		sleep $sched_period
40766dfaacSMichal Berger
41766dfaacSMichal Berger		all_set=0
42766dfaacSMichal Berger		reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
43766dfaacSMichal Berger
44766dfaacSMichal Berger		printf '*Sample %u\n' "$samples"
45766dfaacSMichal Berger		for cpu in "${selected_cpus[@]}"; do
46766dfaacSMichal Berger			threads=($(jq -r "select(.lcore == $cpu) | .lw_threads[].id" <<< "$reactor_framework"))
47766dfaacSMichal Berger
48766dfaacSMichal Berger			if ((${#threads[@]} == 0)); then
49766dfaacSMichal Berger				printf '  No threads found on cpu%u\n' "$cpu"
50766dfaacSMichal Berger				continue
51766dfaacSMichal Berger			fi
52766dfaacSMichal Berger
53766dfaacSMichal Berger			get_thread_stats
54766dfaacSMichal Berger
55766dfaacSMichal Berger			for thread in "${threads[@]}"; do
56766dfaacSMichal Berger				if ((thread != thread0 && thread != thread1)); then
57766dfaacSMichal Berger					printf '  Unexpected thread %u (%s) on cpu%u\n' \
58766dfaacSMichal Berger						"$thread" "${thread_map[thread]}" "$cpu"
59766dfaacSMichal Berger					continue 3
60766dfaacSMichal Berger				fi
61766dfaacSMichal Berger				load=$((busy[thread] * 100 / (busy[thread] + idle[thread])))
62766dfaacSMichal Berger				if ((load < 95)); then
63766dfaacSMichal Berger					printf '  Unexpected load on thread %u (%s): %u%% (< 95%%)\n' \
64766dfaacSMichal Berger						"$thread" "${thread_map[thread]}" "$load"
65766dfaacSMichal Berger					continue 3
66766dfaacSMichal Berger				fi
67766dfaacSMichal Berger				printf '  Thread %u (%s) on cpu%u; load: %u%%\n' \
68766dfaacSMichal Berger					"$thread" "${thread_map[thread]}" "$cpu" "$load"
69766dfaacSMichal Berger				eval "${thread_map[thread]}_cpus[$cpu]=$cpu"
70766dfaacSMichal Berger			done
71766dfaacSMichal Berger		done
72766dfaacSMichal Berger
73766dfaacSMichal Berger		all_set=1
74766dfaacSMichal Berger	done
75766dfaacSMichal Berger
76766dfaacSMichal Berger	destroy_thread "$thread0"
77766dfaacSMichal Berger	destroy_thread "$thread1"
78766dfaacSMichal Berger
79766dfaacSMichal Berger	# The final expectation is that when target threads are ~100% busy, they will stay on their
80766dfaacSMichal Berger	# designated cpus. FIXME: Does it make sense? if given cpu is not getting a break due to a
81766dfaacSMichal Berger	# thread not becoming idle even for a tick, scheduler should not put any other threads on
82766dfaacSMichal Berger	# that cpu nor move its assigned thread to any other cpu.
83766dfaacSMichal Berger	printf 'Thread %u (%s) cpus: %s\n' "$thread0" "${thread_map[thread0]}" "${thread0_cpus[*]:-none}"
84766dfaacSMichal Berger	printf 'Thread %u (%s) cpus: %s\n' "$thread1" "${thread_map[thread1]}" "${thread1_cpus[*]:-none}"
85766dfaacSMichal Berger	[[ ${thread0_cpus[*]} != "${thread1_cpus[*]}" ]]
86766dfaacSMichal Berger	((${#thread0_cpus[@]} == 1 && ${#thread1_cpus[@]} == 1 && all_set == 1))
87766dfaacSMichal Berger
88766dfaacSMichal Berger	xtrace_restore
89766dfaacSMichal Berger}
90766dfaacSMichal Berger
91766dfaacSMichal Bergerbalanced() {
92766dfaacSMichal Berger
93766dfaacSMichal Berger	local thread cpu
94766dfaacSMichal Berger	local extra_threads
955c61089cSMichal Berger	local sched_period=1 # default, 1s
965c61089cSMichal Berger	local active_cpu
97766dfaacSMichal Berger
98766dfaacSMichal Berger	# Exclude main cpu
99766dfaacSMichal Berger	fold_list_onto_array selected_cpus "${cpus[@]:1}"
100766dfaacSMichal Berger
101d47b0d2dSTomasz Zawadzki	thread0=$(create_thread -n "thread0" -m "$(mask_cpus "${selected_cpus[@]}")" -a 0)
102127fc0d0STomasz Zawadzki	for cpu in "${selected_cpus[@]::${#selected_cpus[@]}-1}"; do
103766dfaacSMichal Berger		extra_threads+=("$(create_thread -n "thread_cpu_$cpu" -m "$(mask_cpus "$cpu")" -a 100)")
104766dfaacSMichal Berger	done
105766dfaacSMichal Berger
1065c61089cSMichal Berger	# thread0 is idle, wait for scheduler to run (2x scheduling period) and check if it is on main core
1075c61089cSMichal Berger	sleep $((2 * sched_period))
108766dfaacSMichal Berger	reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
1095c61089cSMichal Berger	[[ -n $(jq -r "select(.lcore == $spdk_main_core) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework"
110766dfaacSMichal Berger
1115c61089cSMichal Berger	# thread0 is active, wait for scheduler to run (2x) and check if it is not on main core
1125c61089cSMichal Berger	active_thread "$thread0" 100
1135c61089cSMichal Berger	sleep $((2 * sched_period))
1145c61089cSMichal Berger	reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
115766dfaacSMichal Berger
1165c61089cSMichal Berger	[[ -z $(jq -r "select(.lcore == $spdk_main_core) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework"
1175c61089cSMichal Berger	# Get the cpu thread was scheduled onto
1185c61089cSMichal Berger	for cpu in "${selected_cpus[@]}"; do
1195c61089cSMichal Berger		[[ -n $(jq -r "select(.lcore == $cpu) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework" && active_cpu=$cpu
1205c61089cSMichal Berger	done
1215c61089cSMichal Berger	[[ -n ${selected_cpus[active_cpu]} ]]
122766dfaacSMichal Berger
1235c61089cSMichal Berger	# thread0 is idle, wait for scheduler to run (2x) and check if it is on main core
1245c61089cSMichal Berger	active_thread "$thread0" 0
1255c61089cSMichal Berger	sleep $((2 * sched_period))
1265c61089cSMichal Berger	reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
1275c61089cSMichal Berger
1285c61089cSMichal Berger	[[ -n $(jq -r "select(.lcore == $spdk_main_core) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework"
1295c61089cSMichal Berger
130127fc0d0STomasz Zawadzki	# thread0 is active, wait for scheduler to run (2x) and check if it is not on main core
1315c61089cSMichal Berger	active_thread "$thread0" 100
1325c61089cSMichal Berger	sleep $((2 * sched_period))
1335c61089cSMichal Berger	reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
1345c61089cSMichal Berger
135127fc0d0STomasz Zawadzki	[[ -z $(jq -r "select(.lcore == $spdk_main_core) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework"
136766dfaacSMichal Berger
137766dfaacSMichal Berger	destroy_thread "$thread0"
138766dfaacSMichal Berger	for thread in "${extra_threads[@]}"; do
139766dfaacSMichal Berger		destroy_thread "$thread"
140766dfaacSMichal Berger	done
141766dfaacSMichal Berger}
142766dfaacSMichal Berger
143d841e24bSMichal Bergercore_load() {
144d841e24bSMichal Berger	local sched_period=1 # default, 1s
145d841e24bSMichal Berger	local thread
146d841e24bSMichal Berger	local on_main_core=0 on_next_core=0
147d841e24bSMichal Berger
148d841e24bSMichal Berger	# Re-exec the scheduler app to make sure rr balancer won't affect threads without
149d841e24bSMichal Berger	# configured cpumask from the previous test suites.
150d841e24bSMichal Berger
151c9c7c281SJosh Soref	exec_under_dynamic_scheduler "$scheduler" -m "$spdk_cpumask" --main-core "$spdk_main_core"
152d841e24bSMichal Berger
153d841e24bSMichal Berger	# Create thread0 with 90% activity no cpumask, expecting it to remain on main cpu
154d841e24bSMichal Berger	thread0=$(create_thread -n "thread0" -a 90)
155d841e24bSMichal Berger
156d841e24bSMichal Berger	sleep $((2 * sched_period))
157d841e24bSMichal Berger	update_thread_cpus_map
158d841e24bSMichal Berger
159d841e24bSMichal Berger	((thread_cpus[thread0] == spdk_main_core))
160d841e24bSMichal Berger
161d841e24bSMichal Berger	# Create thread1 with 90% activity. Expecting one of the threads to be moved to next
162d841e24bSMichal Berger	# cpu and the other remain on main cpu. Verifying that threads are spread out when core
163d841e24bSMichal Berger	# load is over 95% limit.
164d841e24bSMichal Berger	thread1=$(create_thread -n "thread1" -a 90)
165d841e24bSMichal Berger
166d841e24bSMichal Berger	# Three iterations are needed, as both active threads first are moved out of main core.
167d841e24bSMichal Berger	# During next scheduling period one of them is moved back to the main core.
168d841e24bSMichal Berger	sleep $((3 * sched_period))
169d841e24bSMichal Berger	update_thread_cpus_map
170d841e24bSMichal Berger
171d841e24bSMichal Berger	((thread_cpus[thread0] == spdk_main_core || thread_cpus[thread1] == spdk_main_core))
172d841e24bSMichal Berger	((thread_cpus[thread0] != thread_cpus[thread1]))
173d841e24bSMichal Berger
174d841e24bSMichal Berger	# Create thread2 with 10% activity. Expecting the idle thread2 to be placed on main cpu and two
175d841e24bSMichal Berger	# other active threads on next cpus. Verifying the condition where core load over 95% moves threads
176d841e24bSMichal Berger	# away from main cpu.
177d841e24bSMichal Berger	thread2=$(create_thread -n "thread2" -a 10)
178d841e24bSMichal Berger
179d841e24bSMichal Berger	sleep $((2 * sched_period))
180d841e24bSMichal Berger	update_thread_cpus_map
181d841e24bSMichal Berger
182d841e24bSMichal Berger	((thread_cpus[thread2] == spdk_main_core))
183d841e24bSMichal Berger	((thread_cpus[thread1] != spdk_main_core))
184d841e24bSMichal Berger	((thread_cpus[thread0] != spdk_main_core))
185d841e24bSMichal Berger	((thread_cpus[thread0] != thread_cpus[thread1]))
186d841e24bSMichal Berger
187d841e24bSMichal Berger	# Change all threads activity to 10%. Expecting all threads to be placed on main cpu.
188d841e24bSMichal Berger	# Verifying the condition where core load less than 95% is grouping multiple threads.
189d841e24bSMichal Berger	active_thread "$thread0" 10
190d841e24bSMichal Berger	active_thread "$thread1" 10
191d841e24bSMichal Berger	active_thread "$thread2" 10
192d841e24bSMichal Berger
193d841e24bSMichal Berger	sleep $((2 * sched_period))
194d841e24bSMichal Berger	update_thread_cpus_map
195d841e24bSMichal Berger
196d841e24bSMichal Berger	for thread in \
197d841e24bSMichal Berger		"$thread0" \
198d841e24bSMichal Berger		"$thread1" \
199d841e24bSMichal Berger		"$thread2"; do
200d841e24bSMichal Berger		((thread_cpus[thread] == spdk_main_core))
201d841e24bSMichal Berger	done
202d841e24bSMichal Berger
203d841e24bSMichal Berger	# Create thread3, thread4 and thread 5 with 25% activity. Expecting one of the threads on next cpu
204d841e24bSMichal Berger	# and rest on main cpu. Total load on main cpu will be (10*3+25*2) 80%, and next cpu 25%.
205d841e24bSMichal Berger	thread3=$(create_thread -n "thread3" -a 25)
206d841e24bSMichal Berger	thread4=$(create_thread -n "thread4" -a 25)
207d841e24bSMichal Berger	thread5=$(create_thread -n "thread5" -a 25)
208d841e24bSMichal Berger
209d841e24bSMichal Berger	# Three iterations are needed, as all threads look active on first iteration since they are on the main core.
210c9c7c281SJosh Soref	# Second iteration will have them spread out over cores and only third will collapse to the expected scenario.
211d841e24bSMichal Berger	sleep $((3 * sched_period))
212d841e24bSMichal Berger	update_thread_cpus_map
213d841e24bSMichal Berger
214e52df268SMichal Berger	# Verify that load is not exceeding 80% on each of the cpus except the main and next cpu
215e52df268SMichal Berger	get_cpu_time 5 user "${cpus[@]:2}"
216e52df268SMichal Berger
217e52df268SMichal Berger	for cpu in "${!avg_cpu_time[@]}"; do
218e52df268SMichal Berger		printf '* cpu%u avg load: %u%% (%s)\n' \
219e52df268SMichal Berger			"$cpu" "${avg_cpu_time[cpu]}" "${cpu_times[cpu]}"
220e52df268SMichal Berger		((avg_cpu_time[cpu] <= 80))
221e52df268SMichal Berger	done
222e52df268SMichal Berger
223d841e24bSMichal Berger	for thread in \
224d841e24bSMichal Berger		"$thread0" \
225d841e24bSMichal Berger		"$thread1" \
226d841e24bSMichal Berger		"$thread2" \
227d841e24bSMichal Berger		"$thread3" \
228d841e24bSMichal Berger		"$thread4" \
229d841e24bSMichal Berger		"$thread5"; do
230d841e24bSMichal Berger		if ((thread_cpus[thread] == spdk_main_core)); then
231d841e24bSMichal Berger			((++on_main_core))
232d841e24bSMichal Berger		else
233d841e24bSMichal Berger			((++on_next_core))
234d841e24bSMichal Berger		fi
235d841e24bSMichal Berger
236d841e24bSMichal Berger		destroy_thread "$thread"
237d841e24bSMichal Berger	done
238d841e24bSMichal Berger
239d841e24bSMichal Berger	((on_main_core == 5 && on_next_core == 1))
240d841e24bSMichal Berger}
241d841e24bSMichal Berger
242c9c7c281SJosh Sorefexec_under_dynamic_scheduler "$scheduler" -m "$spdk_cpumask" --main-core "$spdk_main_core"
243766dfaacSMichal Berger
244766dfaacSMichal Bergerrun_test "busy" busy
245766dfaacSMichal Bergerrun_test "balanced" balanced
246d841e24bSMichal Bergerrun_test "core_load" core_load
247