Spdk/test/scheduler/load_balancing.sh
paul luse eb53c23236 add (c) and SPDX header to bash files as needed
per Intel policy to include file commit date using git cmd
below.  The policy does not apply to non-Intel (C) notices.

git log --follow -C90% --format=%ad --date default <file> | tail -1

and then pull just the year from the result.

Intel copyrights were not added to files where Intel either had
no contribution ot the contribution lacked substance (ie license
header updates, formatting changes, etc)

For intel copyrights added, --follow and -C95% were used.

Signed-off-by: paul luse <paul.e.luse@intel.com>
Change-Id: I2ef86976095b88a9bf5b1003e59f3943cd6bbe4c
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15209
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Krzysztof Karas <krzysztof.karas@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
2022-11-29 08:27:51 +00:00

247 lines
8.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (C) 2021 Intel Corporation
# All rights reserved.
#
testdir=$(readlink -f "$(dirname "$0")")
rootdir=$(readlink -f "$testdir/../../")
source "$rootdir/test/common/autotest_common.sh"
source "$testdir/common.sh"
trap 'killprocess "$spdk_pid"' EXIT
fold_list_onto_array cpus $(parse_cpu_list <(echo "$spdk_cpus_csv"))
# Normalize the indexes
cpus=("${cpus[@]}")
busy() {
local selected_cpus cpu
local reactor_framework
local threads thread
local sched_period=1 # default, 1s
# Create two busy threads with two cpus (not including main cpu) and check if either of
# them is moved to either of the selected cpus. Expected load is ~100% on each thread and
# each thread should remain on its designated cpu.
fold_list_onto_array selected_cpus "${cpus[@]:1:2}"
thread0=$(create_thread -n "thread0" -m "$(mask_cpus "${selected_cpus[@]}")" -a 100)
thread1=$(create_thread -n "thread1" -m "$(mask_cpus "${selected_cpus[@]}")" -a 100)
sleep $((10 * sched_period))
local samples=0
xtrace_disable
while ((samples++ < 5)); do
sleep $sched_period
all_set=0
reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
printf '*Sample %u\n' "$samples"
for cpu in "${selected_cpus[@]}"; do
threads=($(jq -r "select(.lcore == $cpu) | .lw_threads[].id" <<< "$reactor_framework"))
if ((${#threads[@]} == 0)); then
printf ' No threads found on cpu%u\n' "$cpu"
continue
fi
get_thread_stats
for thread in "${threads[@]}"; do
if ((thread != thread0 && thread != thread1)); then
printf ' Unexpected thread %u (%s) on cpu%u\n' \
"$thread" "${thread_map[thread]}" "$cpu"
continue 3
fi
load=$((busy[thread] * 100 / (busy[thread] + idle[thread])))
if ((load < 95)); then
printf ' Unexpected load on thread %u (%s): %u%% (< 95%%)\n' \
"$thread" "${thread_map[thread]}" "$load"
continue 3
fi
printf ' Thread %u (%s) on cpu%u; load: %u%%\n' \
"$thread" "${thread_map[thread]}" "$cpu" "$load"
eval "${thread_map[thread]}_cpus[$cpu]=$cpu"
done
done
all_set=1
done
destroy_thread "$thread0"
destroy_thread "$thread1"
# The final expectation is that when target threads are ~100% busy, they will stay on their
# designated cpus. FIXME: Does it make sense? if given cpu is not getting a break due to a
# thread not becoming idle even for a tick, scheduler should not put any other threads on
# that cpu nor move its assigned thread to any other cpu.
printf 'Thread %u (%s) cpus: %s\n' "$thread0" "${thread_map[thread0]}" "${thread0_cpus[*]:-none}"
printf 'Thread %u (%s) cpus: %s\n' "$thread1" "${thread_map[thread1]}" "${thread1_cpus[*]:-none}"
[[ ${thread0_cpus[*]} != "${thread1_cpus[*]}" ]]
((${#thread0_cpus[@]} == 1 && ${#thread1_cpus[@]} == 1 && all_set == 1))
xtrace_restore
}
balanced() {
local thread cpu
local extra_threads
local sched_period=1 # default, 1s
local active_cpu
# Exclude main cpu
fold_list_onto_array selected_cpus "${cpus[@]:1}"
thread0=$(create_thread -n "thread0" -m "$(mask_cpus "${selected_cpus[@]}")" -a 0)
for cpu in "${selected_cpus[@]::${#selected_cpus[@]}-1}"; do
extra_threads+=("$(create_thread -n "thread_cpu_$cpu" -m "$(mask_cpus "$cpu")" -a 100)")
done
# thread0 is idle, wait for scheduler to run (2x scheduling period) and check if it is on main core
sleep $((2 * sched_period))
reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
[[ -n $(jq -r "select(.lcore == $spdk_main_core) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework"
# thread0 is active, wait for scheduler to run (2x) and check if it is not on main core
active_thread "$thread0" 100
sleep $((2 * sched_period))
reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
[[ -z $(jq -r "select(.lcore == $spdk_main_core) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework"
# Get the cpu thread was scheduled onto
for cpu in "${selected_cpus[@]}"; do
[[ -n $(jq -r "select(.lcore == $cpu) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework" && active_cpu=$cpu
done
[[ -n ${selected_cpus[active_cpu]} ]]
# thread0 is idle, wait for scheduler to run (2x) and check if it is on main core
active_thread "$thread0" 0
sleep $((2 * sched_period))
reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
[[ -n $(jq -r "select(.lcore == $spdk_main_core) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework"
# thread0 is active, wait for scheduler to run (2x) and check if it is not on main core
active_thread "$thread0" 100
sleep $((2 * sched_period))
reactor_framework=$(rpc_cmd framework_get_reactors | jq -r '.reactors[]')
[[ -z $(jq -r "select(.lcore == $spdk_main_core) | .lw_threads[] | select(.id == $thread0)") ]] <<< "$reactor_framework"
destroy_thread "$thread0"
for thread in "${extra_threads[@]}"; do
destroy_thread "$thread"
done
}
core_load() {
local sched_period=1 # default, 1s
local thread
local on_main_core=0 on_next_core=0
# Re-exec the scheduler app to make sure rr balancer won't affect threads without
# configured cpumask from the previous test suites.
exec_under_dynamic_scheduler "$scheduler" -m "$spdk_cpumask" --main-core "$spdk_main_core"
# Create thread0 with 90% activity no cpumask, expecting it to remain on main cpu
thread0=$(create_thread -n "thread0" -a 90)
sleep $((2 * sched_period))
update_thread_cpus_map
((thread_cpus[thread0] == spdk_main_core))
# Create thread1 with 90% activity. Expecting one of the threads to be moved to next
# cpu and the other remain on main cpu. Verifying that threads are spread out when core
# load is over 95% limit.
thread1=$(create_thread -n "thread1" -a 90)
# Three iterations are needed, as both active threads first are moved out of main core.
# During next scheduling period one of them is moved back to the main core.
sleep $((3 * sched_period))
update_thread_cpus_map
((thread_cpus[thread0] == spdk_main_core || thread_cpus[thread1] == spdk_main_core))
((thread_cpus[thread0] != thread_cpus[thread1]))
# Create thread2 with 10% activity. Expecting the idle thread2 to be placed on main cpu and two
# other active threads on next cpus. Verifying the condition where core load over 95% moves threads
# away from main cpu.
thread2=$(create_thread -n "thread2" -a 10)
sleep $((2 * sched_period))
update_thread_cpus_map
((thread_cpus[thread2] == spdk_main_core))
((thread_cpus[thread1] != spdk_main_core))
((thread_cpus[thread0] != spdk_main_core))
((thread_cpus[thread0] != thread_cpus[thread1]))
# Change all threads activity to 10%. Expecting all threads to be placed on main cpu.
# Verifying the condition where core load less than 95% is grouping multiple threads.
active_thread "$thread0" 10
active_thread "$thread1" 10
active_thread "$thread2" 10
sleep $((2 * sched_period))
update_thread_cpus_map
for thread in \
"$thread0" \
"$thread1" \
"$thread2"; do
((thread_cpus[thread] == spdk_main_core))
done
# Create thread3, thread4 and thread 5 with 25% activity. Expecting one of the threads on next cpu
# and rest on main cpu. Total load on main cpu will be (10*3+25*2) 80%, and next cpu 25%.
thread3=$(create_thread -n "thread3" -a 25)
thread4=$(create_thread -n "thread4" -a 25)
thread5=$(create_thread -n "thread5" -a 25)
# Three iterations are needed, as all threads look active on first iteration since they are on the main core.
# Second iteration will have them spread out over cores and only third will collapse to the expected scenario.
sleep $((3 * sched_period))
update_thread_cpus_map
# Verify that load is not exceeding 80% on each of the cpus except the main and next cpu
get_cpu_time 5 user "${cpus[@]:2}"
for cpu in "${!avg_cpu_time[@]}"; do
printf '* cpu%u avg load: %u%% (%s)\n' \
"$cpu" "${avg_cpu_time[cpu]}" "${cpu_times[cpu]}"
((avg_cpu_time[cpu] <= 80))
done
for thread in \
"$thread0" \
"$thread1" \
"$thread2" \
"$thread3" \
"$thread4" \
"$thread5"; do
if ((thread_cpus[thread] == spdk_main_core)); then
((++on_main_core))
else
((++on_next_core))
fi
destroy_thread "$thread"
done
((on_main_core == 5 && on_next_core == 1))
}
exec_under_dynamic_scheduler "$scheduler" -m "$spdk_cpumask" --main-core "$spdk_main_core"
run_test "busy" busy
run_test "balanced" balanced
run_test "core_load" core_load