#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # # Test for cpuset v2 partition root state (PRS) # # The sched verbose flag can be optionally set so that the console log # can be examined for the correct setting of scheduling domain. #
[[ $(id -u) -eq 0 ]] || skip_test "Test must be run as root!"
# Get wait_inotify location
WAIT_INOTIFY=$(cd $(dirname $0); pwd)/wait_inotify
# Find cgroup v2 mount point
CGROUP2=$(mount -t cgroup2 | head -1 | awk -e '{print $3}')
[[ -n "$CGROUP2" ]] || skip_test "Cgroup v2 mount point not found!"
SUBPARTS_CPUS=$CGROUP2/.__DEBUG__.cpuset.cpus.subpartitions
CPULIST=$(cat $CGROUP2/cpuset.cpus.effective)
NR_CPUS=$(lscpu | grep "^CPU(s):" | sed -e "s/.*:[[:space:]]*//")
[[ $NR_CPUS -lt 8 ]] && skip_test "Test needs at least 8 cpus available!"
# Check to see if /dev/console exists and is writable if [[ -c /dev/console && -w /dev/console ]] then
CONSOLE=/dev/console else
CONSOLE=/dev/null fi
# Set verbose flag and delay factor
PROG=$1
VERBOSE=0
DELAY_FACTOR=1
SCHED_DEBUG= while [[ "$1" = -* ]] do
case "$1" in
-v) ((VERBOSE++)) # Enable sched/verbose can slow thing down
[[ $DELAY_FACTOR -eq 1 ]] &&
DELAY_FACTOR=2
;;
-d) DELAY_FACTOR=$2
shift
;;
*) echo"Usage: $PROG [-v] [-d "
exit
;;
esac
shift done
# Set sched verbose flag if available when "-v" option is specified if [[ $VERBOSE -gt 0 && -d /sys/kernel/debug/sched ]] then # Used to restore the original setting during cleanup
SCHED_DEBUG=$(cat /sys/kernel/debug/sched/verbose) echo Y > /sys/kernel/debug/sched/verbose fi
cd $CGROUP2 echo +cpuset > cgroup.subtree_control
# # If cpuset has been set up and used in child cgroups, we may not be able to # create partition under root cgroup because of the CPU exclusivity rule. # So we are going to skip the test if this is the case. #
[[ -d test ]] || mkdir test echo 0-6 > test/cpuset.cpus echo root > test/cpuset.cpus.partition cat test/cpuset.cpus.partition | grep -q invalid
RESULT=$? echo member > test/cpuset.cpus.partition echo"" > test/cpuset.cpus
[[ $RESULT -eq 0 ]] && skip_test "Child cgroups are using cpuset!"
# # If isolated CPUs have been reserved at boot time (as shown in # cpuset.cpus.isolated), these isolated CPUs should be outside of CPUs 0-8 # that will be used by this script for testing purpose. If not, some of # the tests may fail incorrectly. Wait a bit and retry again just in case # these isolated CPUs are leftover from previous run and have just been # cleaned up earlier in this script. # # These pre-isolated CPUs should stay in an isolated state throughout the # testing process for now. #
BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
[[ -n "$BOOT_ISOLCPUS" ]] && {
sleep 0.5
BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
} if [[ -n "$BOOT_ISOLCPUS" ]] then
[[ $(echo $BOOT_ISOLCPUS | sed -e "s/[,-].*//") -le 8 ]] &&
skip_test "Pre-isolated CPUs ($BOOT_ISOLCPUS) overlap CPUs to be tested" echo"Pre-isolated CPUs: $BOOT_ISOLCPUS" fi
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ -------- # # Incorrect change to cpuset.cpus[.exclusive] invalidates partition root # # Adding CPUs to partition root that are not in parent's # cpuset.cpus is allowed, but those extra CPUs are ignored. "C2-3:P1:S+ C3:P1 . . . C2-4 . . 0 A1:|A2:2-3 A1:P1|A2:P1"
# Taking away all CPUs from parent or itself if there are tasks # will make the partition invalid. "C2-3:P1:S+ C3:P1 . . T C2-3 . . 0 A1:2-3|A2:2-3 A1:P1|A2:P-1" " C3:P1:S+ C3 . . T P1 . . 0 A1:3|A2:3 A1:P1|A2:P-1" "$SETUP_A123_PARTITIONS . T:C2-3 . . . 0 A1:2-3|A2:2-3|A3:3 A1:P1|A2:P-1|A3:P-1" "$SETUP_A123_PARTITIONS . T:C2-3:C1-3 . . . 0 A1:1|A2:2|A3:3 A1:P1|A2:P1|A3:P1"
# Changing a partition root to member makes child partitions invalid "C2-3:P1:S+ C3:P1 . . P0 . . . 0 A1:2-3|A2:3 A1:P0|A2:P-1" "$SETUP_A123_PARTITIONS . C2-3 P0 . . 0 A1:2-3|A2:2-3|A3:3 A1:P1|A2:P0|A3:P-1"
# cpuset.cpus can contains cpus not in parent's cpuset.cpus as long # as they overlap. "C2-3:P1:S+ . . . . C3-4:P1 . . 0 A1:2|A2:3 A1:P1|A2:P1"
# Deletion of CPUs distributed to child cgroup is allowed. "C0-1:P1:S+ C1 . C2-3 C4-5 . . . 0 A1:4-5|A2:4-5"
# To become a valid partition root, cpuset.cpus must overlap parent's # cpuset.cpus. " C0-1:P1 . . C2-3 S+ C4-5:P1 . . 0 A1:0-1|A2:0-1 A1:P1|A2:P-1"
# Enabling partition with child cpusets is allowed " C0-1:S+ C1 . C2-3 P1 . . . 0 A1:0-1|A2:1 A1:P1"
# A partition root with non-partition root parent is invalid| but it # can be made valid if its parent becomes a partition root too. " C0-1:S+ C1 . C2-3 . P2 . . 0 A1:0-1|A2:1 A1:P0|A2:P-2" " C0-1:S+ C1:P2 . C2-3 P1 . . . 0 A1:0|A2:1 A1:P1|A2:P2 0-1|1"
# cpuset.cpus can overlap with sibling cpuset.cpus.exclusive but not subsumed by it " C0-3 . . C4-5 X5 . . . 0 A1:0-3|B1:4-5"
# Child partition root that try to take all CPUs from parent partition # with tasks will remain invalid. " C1-4:P1:S+ P1 . . . . . . 0 A1:1-4|A2:1-4 A1:P1|A2:P-1" " C1-4:P1:S+ P1 . . . C1-4 . . 0 A1|A2:1-4 A1:P1|A2:P1" " C1-4:P1:S+ P1 . . T C1-4 . . 0 A1:1-4|A2:1-4 A1:P1|A2:P-1"
# Clearing of cpuset.cpus with a preset cpuset.cpus.exclusive shouldn't # affect cpuset.cpus.exclusive.effective. " C1-4:X3:S+ C1:X3 . . . C . . 0 A2:1-4|XA2:3"
# cpuset.cpus can contain CPUs that overlap a sibling cpuset with cpus.exclusive # but creating a local partition out of it is not allowed. Similarly and change # in cpuset.cpus of a local partition that overlaps sibling exclusive CPUs will # invalidate it. " CX1-4:S+ CX2-4:P2 . C5-6 . . . P1 0 A1:1|A2:2-4|B1:5-6|XB1:5-6 \
A1:P0|A2:P2:B1:P1 2-4" " CX1-4:S+ CX2-4:P2 . C3-6 . . . P1 0 A1:1|A2:2-4|B1:5-6 \
A1:P0|A2:P2:B1:P-1 2-4" " CX1-4:S+ CX2-4:P2 . C5-6 . . . P1:C3-6 0 A1:1|A2:2-4|B1:5-6 \
A1:P0|A2:P2:B1:P-1 2-4"
# # For root partition, dump sched-domains info to console if # verbose mode set for manual comparison with sched debug info. #
[[ $VAL -eq 1 && $VERBOSE -gt 0 ]] && {
DOMS=$(cat $CGRP_DIR/cpuset.cpus.effective)
[[ -n "$DOMS" ]] && echo" [$CGRP_DIR] sched-domain: $DOMS" > $CONSOLE
} done
return 0
}
# # Get isolated (including offline) CPUs by looking at # /sys/kernel/debug/sched/domains and cpuset.cpus.isolated control file, # if available, and compare that with the expected value. # # Note that isolated CPUs from the sched/domains context include offline # CPUs as well as CPUs in non-isolated 1-CPU partition. Those CPUs may # not be included in the cpuset.cpus.isolated control file which contains # only CPUs in isolated partitions as well as those that are isolated at # boot time. # # $1 - expected isolated cpu list(s) <isolcpus1>{,<isolcpus2>} # <isolcpus1> - expected sched/domains value # <isolcpus2> - cpuset.cpus.isolated value = <isolcpus1> if not defined #
check_isolcpus()
{
EXPECTED_ISOLCPUS=$1
ISCPUS=${CGROUP2}/cpuset.cpus.isolated
ISOLCPUS=$(cat $ISCPUS)
LASTISOLCPU=
SCHED_DOMAINS=/sys/kernel/debug/sched/domains if [[ $EXPECTED_ISOLCPUS = . ]] then
EXPECTED_ISOLCPUS=
EXPECTED_SDOMAIN= elif [[ $(expr $EXPECTED_ISOLCPUS : ".*|.*") > 0 ]] then
set -- $(echo $EXPECTED_ISOLCPUS | sed -e "s/|/ /g")
EXPECTED_ISOLCPUS=$2
EXPECTED_SDOMAIN=$1 else
EXPECTED_SDOMAIN=$EXPECTED_ISOLCPUS fi
# # Appending pre-isolated CPUs # Even though CPU #8 isn't used for testing, it can't be pre-isolated # to make appending those CPUs easier. #
[[ -n "$BOOT_ISOLCPUS" ]] && {
EXPECTED_ISOLCPUS=${EXPECTED_ISOLCPUS:+${EXPECTED_ISOLCPUS},}${BOOT_ISOLCPUS}
EXPECTED_SDOMAIN=${EXPECTED_SDOMAIN:+${EXPECTED_SDOMAIN},}${BOOT_ISOLCPUS}
}
# # Check cpuset.cpus.isolated cpumask #
[[ "$EXPECTED_ISOLCPUS" != "$ISOLCPUS" ]] && { # Take a 50ms pause and try again
pause 0.05
ISOLCPUS=$(cat $ISCPUS)
}
[[ "$EXPECTED_ISOLCPUS" != "$ISOLCPUS" ]] && return 1
ISOLCPUS=
EXPECTED_ISOLCPUS=$EXPECTED_SDOMAIN
# # Use the sched domain in debugfs to check isolated CPUs, if available #
[[ -d $SCHED_DOMAINS ]] || return 0
for ((CPU=0; CPU < $NR_CPUS; CPU++)) do
[[ -n "$(ls ${SCHED_DOMAINS}/cpu$CPU)" ]] && continue
if [[ -z "$LASTISOLCPU" ]] then
ISOLCPUS=$CPU
LASTISOLCPU=$CPU elif [[ "$LASTISOLCPU" -eq $((CPU - 1)) ]] then echo $ISOLCPUS | grep -q "\<$LASTISOLCPU\$" if [[ $? -eq 0 ]] then
ISOLCPUS=${ISOLCPUS}- fi
LASTISOLCPU=$CPU else if [[ $ISOLCPUS = *- ]] then
ISOLCPUS=${ISOLCPUS}$LASTISOLCPU fi
ISOLCPUS=${ISOLCPUS},$CPU
LASTISOLCPU=$CPU fi done
[[ "$ISOLCPUS" = *- ]] && ISOLCPUS=${ISOLCPUS}$LASTISOLCPU
# # Check to see if there are unexpected isolated CPUs left beyond the boot # time isolated ones. #
null_isolcpus_check()
{
[[ $VERBOSE -gt 0 ]] || return 0 # Retry a few times before printing error
RETRY=0 while [[ $RETRY -lt 8 ]] do
pause 0.02
check_isolcpus "."
[[ $? -eq 0 ]] && return 0
((RETRY++)) done echo"Unexpected isolated CPUs: $ISOLCPUS"
dump_states
exit 1
}
# # Check state transition test result # $1 - Test number # $2 - Expected effective CPU values # $3 - Expected partition states # $4 - Expected isolated CPUs #
check_test_results()
{
_NR=$1
_ECPUS="$2"
_PSTATES="$3"
_ISOLCPUS="$4"
# Compare the expected isolated CPUs with the actual ones, # if available
[[ -n "$_ISOLCPUS" ]] && {
check_isolcpus $_ISOLCPUS
[[ $? -ne 0 ]] && {
[[ -n "$BOOT_ISOLCPUS" ]] && _ISOLCPUS=${_ISOLCPUS},${BOOT_ISOLCPUS}
test_fail $_NR "isolated CPU" \ "Expect $_ISOLCPUS, get $ISOLCPUS instead"
}
}
reset_cgroup_states # # Check to see if effective cpu list changes #
_NEWLIST=$(cat $CGROUP2/cpuset.cpus.effective)
RETRY=0 while [[ $_NEWLIST != $CPULIST && $RETRY -lt 8 ]] do # Wait a bit longer & recheck a few times
pause 0.02
((RETRY++))
_NEWLIST=$(cat $CGROUP2/cpuset.cpus.effective) done
[[ $_NEWLIST != $CPULIST ]] && { echo"Effective cpus changed to $_NEWLIST after test $_NR!"
exit 1
}
null_isolcpus_check
[[ $VERBOSE -gt 0 ]] && echo"Test $I done."
}
# # Run cpuset state transition test # $1 - test matrix name # # This test is somewhat fragile as delays (sleep x) are added in various # places to make sure state changes are fully propagated before the next # action. These delays may need to be adjusted if running in a slower machine. #
run_state_test()
{
TEST=$1
CONTROLLER=cpuset
CGROUP_LIST=". A1 A1/A2 A1/A2/A3 B1"
RESET_LIST="A1/A2/A3 A1/A2 A1 B1"
I=0 eval CNT="\${#$TEST[@]}"
reset_cgroup_states
console_msg "Running state transition test ..."
while [[ $I -lt $CNT ]] do echo"Running test $I ..." > $CONSOLE
[[ $VERBOSE -gt 1 ]] && { echo"" evalecho \${$TEST[$I]}
} eval set -- "\${$TEST[$I]}"
OLD_A1=$1
OLD_A2=$2
OLD_A3=$3
OLD_B1=$4
NEW_A1=$5
NEW_A2=$6
NEW_A3=$7
NEW_B1=$8
RESULT=$9
ECPUS=${10}
STATES=${11}
ICPUS=${12}
check_test_results $I "$ECPUS""$STATES""$ICPUS"
((I++)) done
cd ..
rmdir rtest echo"All $I tests of $TEST PASSED."
}
# # Testing the new "isolated" partition root type #
test_isolated()
{
cd $CGROUP2/test echo 2-3 > cpuset.cpus
TYPE=$(cat cpuset.cpus.partition)
[[ $TYPE = member ]] || echo member > cpuset.cpus.partition
console_msg "Change from member to root"
test_partition root
console_msg "Change from root to isolated"
test_partition isolated
console_msg "Change from isolated to member"
test_partition member
console_msg "Change from member to isolated"
test_partition isolated
console_msg "Change from isolated to root"
test_partition root
console_msg "Change from root to member"
test_partition member
# # Testing partition root with no cpu #
console_msg "Distribute all cpus to child partition" echo +cpuset > cgroup.subtree_control
test_partition root
mkdir A1
cd A1 echo 2-3 > cpuset.cpus
test_partition root
test_effective_cpus 2-3
cd ..
test_effective_cpus ""
console_msg "Moving task to partition test"
test_add_proc "No space left"
cd A1
test_add_proc ""
cd ..
console_msg "Shrink and expand child partition"
cd A1 echo 2 > cpuset.cpus
cd ..
test_effective_cpus 3
cd A1 echo 2-3 > cpuset.cpus
cd ..
test_effective_cpus ""
# # Wait for inotify event for the given file and read it # $1: cgroup file to wait for # $2: file to store the read result #
wait_inotify()
{
CGROUP_FILE=$1
OUTPUT_FILE=$2
# # Test if inotify events are properly generated when going into and out of # invalid partition state. #
test_inotify()
{
ERR=0
PRS=/tmp/.prs_$$
cd $CGROUP2/test
[[ -f $WAIT_INOTIFY ]] || { echo"wait_inotify not found, inotify test SKIPPED."
return
}
pause 0.01 echo 1 > cpuset.cpus echo 0 > cgroup.procs echo root > cpuset.cpus.partition
pause 0.01 rm -f $PRS
wait_inotify $PWD/cpuset.cpus.partition $PRS &
pause 0.01
set_ctrl_state . "O1=0"
pause 0.01
check_cgroup_states ".:P-1" if [[ $? -ne 0 ]] then echo"FAILED: Inotify test - partition not invalid"
ERR=1 elif [[ ! -f $PRS ]] then echo"FAILED: Inotify test - event not generated"
ERR=1
kill %1 elif [[ $(cat $PRS) != "root invalid"* ]] then echo"FAILED: Inotify test - incorrect state" cat $PRS
ERR=1 fi
online_cpus echo member > cpuset.cpus.partition echo 0 > ../cgroup.procs if [[ $ERR -ne 0 ]] then
exit 1 else echo"Inotify test PASSED" fi echo member > cpuset.cpus.partition echo"" > cpuset.cpus
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.