| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006 |
- #!/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.
- #
- skip_test() {
- echo "$1"
- echo "Test SKIPPED"
- exit 4 # ksft_skip
- }
- [[ $(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 <delay-factor>"
- 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. These pre-isolated CPUs should stay in
- # an isolated state throughout the testing process for now.
- #
- 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
- cleanup()
- {
- online_cpus
- cd $CGROUP2
- rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1
- rmdir test > /dev/null 2>&1
- [[ -n "$SCHED_DEBUG" ]] &&
- echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose
- }
- # Pause in ms
- pause()
- {
- DELAY=$1
- LOOP=0
- while [[ $LOOP -lt $DELAY_FACTOR ]]
- do
- sleep $DELAY
- ((LOOP++))
- done
- return 0
- }
- console_msg()
- {
- MSG=$1
- echo "$MSG"
- echo "" > $CONSOLE
- echo "$MSG" > $CONSOLE
- pause 0.01
- }
- test_partition()
- {
- EXPECTED_VAL=$1
- echo $EXPECTED_VAL > cpuset.cpus.partition
- [[ $? -eq 0 ]] || exit 1
- ACTUAL_VAL=$(cat cpuset.cpus.partition)
- [[ $ACTUAL_VAL != $EXPECTED_VAL ]] && {
- echo "cpuset.cpus.partition: expect $EXPECTED_VAL, found $ACTUAL_VAL"
- echo "Test FAILED"
- exit 1
- }
- }
- test_effective_cpus()
- {
- EXPECTED_VAL=$1
- ACTUAL_VAL=$(cat cpuset.cpus.effective)
- [[ "$ACTUAL_VAL" != "$EXPECTED_VAL" ]] && {
- echo "cpuset.cpus.effective: expect '$EXPECTED_VAL', found '$ACTUAL_VAL'"
- echo "Test FAILED"
- exit 1
- }
- }
- # Adding current process to cgroup.procs as a test
- test_add_proc()
- {
- OUTSTR="$1"
- ERRMSG=$((echo $$ > cgroup.procs) |& cat)
- echo $ERRMSG | grep -q "$OUTSTR"
- [[ $? -ne 0 ]] && {
- echo "cgroup.procs: expect '$OUTSTR', got '$ERRMSG'"
- echo "Test FAILED"
- exit 1
- }
- echo $$ > $CGROUP2/cgroup.procs # Move out the task
- }
- #
- # Cpuset controller state transition test matrix.
- #
- # Cgroup test hierarchy
- #
- # root -- A1 -- A2 -- A3
- # +- B1
- #
- # P<v> = set cpus.partition (0:member, 1:root, 2:isolated)
- # C<l> = add cpu-list to cpuset.cpus
- # X<l> = add cpu-list to cpuset.cpus.exclusive
- # S<p> = use prefix in subtree_control
- # T = put a task into cgroup
- # O<c>=<v> = Write <v> to CPU online file of <c>
- #
- # ECPUs - effective CPUs of cpusets
- # Pstate - partition root state
- # ISOLCPUS - isolated CPUs (<icpus>[,<icpus2>])
- #
- # Note that if there are 2 fields in ISOLCPUS, the first one is for
- # sched-debug matching which includes offline CPUs and single-CPU partitions
- # while the second one is for matching cpuset.cpus.isolated.
- #
- SETUP_A123_PARTITIONS="C1-3:P1:S+ C2-3:P1:S+ C3:P1"
- TEST_MATRIX=(
- # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
- # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
- " C0-1 . . C2-3 S+ C4-5 . . 0 A2:0-1"
- " C0-1 . . C2-3 P1 . . . 0 "
- " C0-1 . . C2-3 P1:S+ C0-1:P1 . . 0 "
- " C0-1 . . C2-3 P1:S+ C1:P1 . . 0 "
- " C0-1:S+ . . C2-3 . . . P1 0 "
- " C0-1:P1 . . C2-3 S+ C1 . . 0 "
- " C0-1:P1 . . C2-3 S+ C1:P1 . . 0 "
- " C0-1:P1 . . C2-3 S+ C1:P1 . P1 0 "
- " C0-1:P1 . . C2-3 C4-5 . . . 0 A1:4-5"
- " C0-1:P1 . . C2-3 S+:C4-5 . . . 0 A1:4-5"
- " C0-1 . . C2-3:P1 . . . C2 0 "
- " C0-1 . . C2-3:P1 . . . C4-5 0 B1:4-5"
- "C0-3:P1:S+ C2-3:P1 . . . . . . 0 A1:0-1,A2:2-3"
- "C0-3:P1:S+ C2-3:P1 . . C1-3 . . . 0 A1:1,A2:2-3"
- "C2-3:P1:S+ C3:P1 . . C3 . . . 0 A1:,A2:3 A1:P1,A2:P1"
- "C2-3:P1:S+ C3:P1 . . C3 P0 . . 0 A1:3,A2:3 A1:P1,A2:P0"
- "C2-3:P1:S+ C2:P1 . . C2-4 . . . 0 A1:3-4,A2:2"
- "C2-3:P1:S+ C3:P1 . . C3 . . C0-2 0 A1:,B1:0-2 A1:P1,A2:P1"
- "$SETUP_A123_PARTITIONS . C2-3 . . . 0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
- # CPU offlining cases:
- " C0-1 . . C2-3 S+ C4-5 . O2=0 0 A1:0-1,B1:3"
- "C0-3:P1:S+ C2-3:P1 . . O2=0 . . . 0 A1:0-1,A2:3"
- "C0-3:P1:S+ C2-3:P1 . . O2=0 O2=1 . . 0 A1:0-1,A2:2-3"
- "C0-3:P1:S+ C2-3:P1 . . O1=0 . . . 0 A1:0,A2:2-3"
- "C0-3:P1:S+ C2-3:P1 . . O1=0 O1=1 . . 0 A1:0-1,A2:2-3"
- "C2-3:P1:S+ C3:P1 . . O3=0 O3=1 . . 0 A1:2,A2:3 A1:P1,A2:P1"
- "C2-3:P1:S+ C3:P2 . . O3=0 O3=1 . . 0 A1:2,A2:3 A1:P1,A2:P2"
- "C2-3:P1:S+ C3:P1 . . O2=0 O2=1 . . 0 A1:2,A2:3 A1:P1,A2:P1"
- "C2-3:P1:S+ C3:P2 . . O2=0 O2=1 . . 0 A1:2,A2:3 A1:P1,A2:P2"
- "C2-3:P1:S+ C3:P1 . . O2=0 . . . 0 A1:,A2:3 A1:P1,A2:P1"
- "C2-3:P1:S+ C3:P1 . . O3=0 . . . 0 A1:2,A2: A1:P1,A2:P1"
- "C2-3:P1:S+ C3:P1 . . T:O2=0 . . . 0 A1:3,A2:3 A1:P1,A2:P-1"
- "C2-3:P1:S+ C3:P1 . . . T:O3=0 . . 0 A1:2,A2:2 A1:P1,A2:P-1"
- "$SETUP_A123_PARTITIONS . O1=0 . . . 0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
- "$SETUP_A123_PARTITIONS . O2=0 . . . 0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1"
- "$SETUP_A123_PARTITIONS . O3=0 . . . 0 A1:1,A2:2,A3: A1:P1,A2:P1,A3:P1"
- "$SETUP_A123_PARTITIONS . T:O1=0 . . . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1"
- "$SETUP_A123_PARTITIONS . . T:O2=0 . . 0 A1:1,A2:3,A3:3 A1:P1,A2:P1,A3:P-1"
- "$SETUP_A123_PARTITIONS . . . T:O3=0 . 0 A1:1,A2:2,A3:2 A1:P1,A2:P1,A3:P-1"
- "$SETUP_A123_PARTITIONS . T:O1=0 O1=1 . . 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
- "$SETUP_A123_PARTITIONS . . T:O2=0 O2=1 . 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
- "$SETUP_A123_PARTITIONS . . . T:O3=0 O3=1 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
- "$SETUP_A123_PARTITIONS . T:O1=0 O2=0 O1=1 . 0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1"
- "$SETUP_A123_PARTITIONS . T:O1=0 O2=0 O2=1 . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1"
- # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
- # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
- #
- # Remote partition and cpuset.cpus.exclusive tests
- #
- " C0-3:S+ C1-3:S+ C2-3 . X2-3 . . . 0 A1:0-3,A2:1-3,A3:2-3,XA1:2-3"
- " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3:P2 . . 0 A1:0-1,A2:2-3,A3:2-3 A1:P0,A2:P2 2-3"
- " C0-3:S+ C1-3:S+ C2-3 . X2-3 X3:P2 . . 0 A1:0-2,A2:3,A3:3 A1:P0,A2:P2 3"
- " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2 . 0 A1:0-1,A2:1,A3:2-3 A1:P0,A3:P2 2-3"
- " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:C3 . 0 A1:0-1,A2:1,A3:2-3 A1:P0,A3:P2 2-3"
- " C0-3:S+ C1-3:S+ C2-3 C2-3 . . . P2 0 A1:0-3,A2:1-3,A3:2-3,B1:2-3 A1:P0,A3:P0,B1:P-2"
- " C0-3:S+ C1-3:S+ C2-3 C4-5 . . . P2 0 B1:4-5 B1:P2 4-5"
- " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3 X2-3:P2 P2 0 A3:2-3,B1:4 A3:P2,B1:P2 2-4"
- " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3 X2-3:P2:C1-3 P2 0 A3:2-3,B1:4 A3:P2,B1:P2 2-4"
- " C0-3:S+ C1-3:S+ C2-3 C4 X1-3 X1-3:P2 P2 . 0 A2:1,A3:2-3 A2:P2,A3:P2 1-3"
- " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3 X2-3:P2 P2:C4-5 0 A3:2-3,B1:4-5 A3:P2,B1:P2 2-5"
- " C4:X0-3:S+ X1-3:S+ X2-3 . . P2 . . 0 A1:4,A2:1-3,A3:1-3 A2:P2 1-3"
- " C4:X0-3:S+ X1-3:S+ X2-3 . . . P2 . 0 A1:4,A2:4,A3:2-3 A3:P2 2-3"
- # Nested remote/local partition tests
- " C0-3:S+ C1-3:S+ C2-3 C4-5 X2-3 X2-3:P1 P2 P1 0 A1:0-1,A2:,A3:2-3,B1:4-5 \
- A1:P0,A2:P1,A3:P2,B1:P1 2-3"
- " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3:P1 P2 P1 0 A1:0-1,A2:,A3:2-3,B1:4 \
- A1:P0,A2:P1,A3:P2,B1:P1 2-4,2-3"
- " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3:P1 . P1 0 A1:0-1,A2:2-3,A3:2-3,B1:4 \
- A1:P0,A2:P1,A3:P0,B1:P1"
- " C0-3:S+ C1-3:S+ C3 C4 X2-3 X2-3:P1 P2 P1 0 A1:0-1,A2:2,A3:3,B1:4 \
- A1:P0,A2:P1,A3:P2,B1:P1 2-4,3"
- " C0-4:S+ C1-4:S+ C2-4 . X2-4 X2-4:P2 X4:P1 . 0 A1:0-1,A2:2-3,A3:4 \
- A1:P0,A2:P2,A3:P1 2-4,2-3"
- " C0-4:S+ C1-4:S+ C2-4 . X2-4 X2-4:P2 X3-4:P1 . 0 A1:0-1,A2:2,A3:3-4 \
- A1:P0,A2:P2,A3:P1 2"
- " C0-4:X2-4:S+ C1-4:X2-4:S+:P2 C2-4:X4:P1 \
- . . X5 . . 0 A1:0-4,A2:1-4,A3:2-4 \
- A1:P0,A2:P-2,A3:P-1"
- " C0-4:X2-4:S+ C1-4:X2-4:S+:P2 C2-4:X4:P1 \
- . . . X1 . 0 A1:0-1,A2:2-4,A3:2-4 \
- A1:P0,A2:P2,A3:P-1 2-4"
- # Remote partition offline tests
- " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:O2=0 . 0 A1:0-1,A2:1,A3:3 A1:P0,A3:P2 2-3"
- " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:O2=0 O2=1 0 A1:0-1,A2:1,A3:2-3 A1:P0,A3:P2 2-3"
- " C0-3:S+ C1-3:S+ C3 . X2-3 X2-3 P2:O3=0 . 0 A1:0-2,A2:1-2,A3: A1:P0,A3:P2 3"
- " C0-3:S+ C1-3:S+ C3 . X2-3 X2-3 T:P2:O3=0 . 0 A1:0-2,A2:1-2,A3:1-2 A1:P0,A3:P-2 3,"
- # An invalidated remote partition cannot self-recover from hotplug
- " C0-3:S+ C1-3:S+ C2 . X2-3 X2-3 T:P2:O2=0 O2=1 0 A1:0-3,A2:1-3,A3:2 A1:P0,A3:P-2"
- # cpus.exclusive.effective clearing test
- " C0-3:S+ C1-3:S+ C2 . X2-3:X . . . 0 A1:0-3,A2:1-3,A3:2,XA1:"
- # Invalid to valid remote partition transition test
- " C0-3:S+ C1-3 . . . X3:P2 . . 0 A1:0-3,A2:1-3,XA2: A2:P-2"
- " C0-3:S+ C1-3:X3:P2
- . . X2-3 P2 . . 0 A1:0-2,A2:3,XA2:3 A2:P2 3"
- # Invalid to valid local partition direct transition tests
- " C1-3:S+:P2 X4:P2 . . . . . . 0 A1:1-3,XA1:1-3,A2:1-3:XA2: A1:P2,A2:P-2 1-3"
- " C1-3:S+:P2 X4:P2 . . . X3:P2 . . 0 A1:1-2,XA1:1-3,A2:3:XA2:3 A1:P2,A2:P2 1-3"
- " C0-3:P2 . . C4-6 C0-4 . . . 0 A1:0-4,B1:4-6 A1:P-2,B1:P0"
- " C0-3:P2 . . C4-6 C0-4:C0-3 . . . 0 A1:0-3,B1:4-6 A1:P2,B1:P0 0-3"
- " C0-3:P2 . . C3-5:C4-5 . . . . 0 A1:0-3,B1:4-5 A1:P2,B1:P0 0-3"
- # Local partition invalidation tests
- " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \
- . . . . . 0 A1:1,A2:2,A3:3 A1:P2,A2:P2,A3:P2 1-3"
- " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \
- . . X4 . . 0 A1:1-3,A2:1-3,A3:2-3,XA2:,XA3: A1:P2,A2:P-2,A3:P-2 1-3"
- " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \
- . . C4:X . . 0 A1:1-3,A2:1-3,A3:2-3,XA2:,XA3: A1:P2,A2:P-2,A3:P-2 1-3"
- # Local partition CPU change tests
- " C0-5:S+:P2 C4-5:S+:P1 . . . C3-5 . . 0 A1:0-2,A2:3-5 A1:P2,A2:P1 0-2"
- " C0-5:S+:P2 C4-5:S+:P1 . . C1-5 . . . 0 A1:1-3,A2:4-5 A1:P2,A2:P1 1-3"
- # cpus_allowed/exclusive_cpus update tests
- " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \
- . X:C4 . P2 . 0 A1:4,A2:4,XA2:,XA3:,A3:4 \
- A1:P0,A3:P-2"
- " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \
- . X1 . P2 . 0 A1:0-3,A2:1-3,XA1:1,XA2:,XA3:,A3:2-3 \
- A1:P0,A3:P-2"
- " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \
- . . X3 P2 . 0 A1:0-2,A2:1-2,XA2:3,XA3:3,A3:3 \
- A1:P0,A3:P2 3"
- " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \
- . . X3 . . 0 A1:0-3,A2:1-3,XA2:3,XA3:3,A3:2-3 \
- A1:P0,A3:P-2"
- " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \
- . X4 . . . 0 A1:0-3,A2:1-3,A3:2-3,XA1:4,XA2:,XA3 \
- A1:P0,A3:P-2"
- # 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"
- # A non-exclusive cpuset.cpus change will invalidate partition and its siblings
- " C0-1:P1 . . C2-3 C0-2 . . . 0 A1:0-2,B1:2-3 A1:P-1,B1:P0"
- " C0-1:P1 . . P1:C2-3 C0-2 . . . 0 A1:0-2,B1:2-3 A1:P-1,B1:P-1"
- " C0-1 . . P1:C2-3 C0-2 . . . 0 A1:0-2,B1:2-3 A1:P0,B1:P-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"
- # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
- # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
- # Failure cases:
- # A task cannot be added to a partition with no cpu
- "C2-3:P1:S+ C3:P1 . . O2=0:T . . . 1 A1:,A2:3 A1:P1,A2:P1"
- # Changes to cpuset.cpus.exclusive that violate exclusivity rule is rejected
- " C0-3 . . C4-5 X0-3 . . X3-5 1 A1:0-3,B1:4-5"
- # cpuset.cpus cannot be a subset of sibling cpuset.cpus.exclusive
- " C0-3 . . C4-5 X3-5 . . . 1 A1:0-3,B1:4-5"
- )
- #
- # Write to the cpu online file
- # $1 - <c>=<v> where <c> = cpu number, <v> value to be written
- #
- write_cpu_online()
- {
- CPU=${1%=*}
- VAL=${1#*=}
- CPUFILE=//sys/devices/system/cpu/cpu${CPU}/online
- if [[ $VAL -eq 0 ]]
- then
- OFFLINE_CPUS="$OFFLINE_CPUS $CPU"
- else
- [[ -n "$OFFLINE_CPUS" ]] && {
- OFFLINE_CPUS=$(echo $CPU $CPU $OFFLINE_CPUS | fmt -1 |\
- sort | uniq -u)
- }
- fi
- echo $VAL > $CPUFILE
- pause 0.05
- }
- #
- # Set controller state
- # $1 - cgroup directory
- # $2 - state
- # $3 - showerr
- #
- # The presence of ":" in state means transition from one to the next.
- #
- set_ctrl_state()
- {
- TMPMSG=/tmp/.msg_$$
- CGRP=$1
- STATE=$2
- SHOWERR=${3}
- CTRL=${CTRL:=$CONTROLLER}
- HASERR=0
- REDIRECT="2> $TMPMSG"
- [[ -z "$STATE" || "$STATE" = '.' ]] && return 0
- [[ $VERBOSE -gt 0 ]] && SHOWERR=1
- rm -f $TMPMSG
- for CMD in $(echo $STATE | sed -e "s/:/ /g")
- do
- TFILE=$CGRP/cgroup.procs
- SFILE=$CGRP/cgroup.subtree_control
- PFILE=$CGRP/cpuset.cpus.partition
- CFILE=$CGRP/cpuset.cpus
- XFILE=$CGRP/cpuset.cpus.exclusive
- S=$(expr substr $CMD 1 1)
- if [[ $S = S ]]
- then
- PREFIX=${CMD#?}
- COMM="echo ${PREFIX}${CTRL} > $SFILE"
- eval $COMM $REDIRECT
- elif [[ $S = X ]]
- then
- CPUS=${CMD#?}
- COMM="echo $CPUS > $XFILE"
- eval $COMM $REDIRECT
- elif [[ $S = C ]]
- then
- CPUS=${CMD#?}
- COMM="echo $CPUS > $CFILE"
- eval $COMM $REDIRECT
- elif [[ $S = P ]]
- then
- VAL=${CMD#?}
- case $VAL in
- 0) VAL=member
- ;;
- 1) VAL=root
- ;;
- 2) VAL=isolated
- ;;
- *)
- echo "Invalid partition state - $VAL"
- exit 1
- ;;
- esac
- COMM="echo $VAL > $PFILE"
- eval $COMM $REDIRECT
- elif [[ $S = O ]]
- then
- VAL=${CMD#?}
- write_cpu_online $VAL
- elif [[ $S = T ]]
- then
- COMM="echo 0 > $TFILE"
- eval $COMM $REDIRECT
- fi
- RET=$?
- [[ $RET -ne 0 ]] && {
- [[ -n "$SHOWERR" ]] && {
- echo "$COMM"
- cat $TMPMSG
- }
- HASERR=1
- }
- pause 0.01
- rm -f $TMPMSG
- done
- return $HASERR
- }
- set_ctrl_state_noerr()
- {
- CGRP=$1
- STATE=$2
- [[ -d $CGRP ]] || mkdir $CGRP
- set_ctrl_state $CGRP $STATE 1
- [[ $? -ne 0 ]] && {
- echo "ERROR: Failed to set $2 to cgroup $1!"
- exit 1
- }
- }
- online_cpus()
- {
- [[ -n "OFFLINE_CPUS" ]] && {
- for C in $OFFLINE_CPUS
- do
- write_cpu_online ${C}=1
- done
- }
- }
- #
- # Return 1 if the list of effective cpus isn't the same as the initial list.
- #
- reset_cgroup_states()
- {
- echo 0 > $CGROUP2/cgroup.procs
- online_cpus
- rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1
- pause 0.02
- set_ctrl_state . R-
- pause 0.01
- }
- dump_states()
- {
- for DIR in . A1 A1/A2 A1/A2/A3 B1
- do
- CPUS=$DIR/cpuset.cpus
- ECPUS=$DIR/cpuset.cpus.effective
- XCPUS=$DIR/cpuset.cpus.exclusive
- XECPUS=$DIR/cpuset.cpus.exclusive.effective
- PRS=$DIR/cpuset.cpus.partition
- PCPUS=$DIR/.__DEBUG__.cpuset.cpus.subpartitions
- ISCPUS=$DIR/cpuset.cpus.isolated
- [[ -e $CPUS ]] && echo "$CPUS: $(cat $CPUS)"
- [[ -e $XCPUS ]] && echo "$XCPUS: $(cat $XCPUS)"
- [[ -e $ECPUS ]] && echo "$ECPUS: $(cat $ECPUS)"
- [[ -e $XECPUS ]] && echo "$XECPUS: $(cat $XECPUS)"
- [[ -e $PRS ]] && echo "$PRS: $(cat $PRS)"
- [[ -e $PCPUS ]] && echo "$PCPUS: $(cat $PCPUS)"
- [[ -e $ISCPUS ]] && echo "$ISCPUS: $(cat $ISCPUS)"
- done
- }
- #
- # Check effective cpus
- # $1 - check string, format: <cgroup>:<cpu-list>[,<cgroup>:<cpu-list>]*
- #
- check_effective_cpus()
- {
- CHK_STR=$1
- for CHK in $(echo $CHK_STR | sed -e "s/,/ /g")
- do
- set -- $(echo $CHK | sed -e "s/:/ /g")
- CGRP=$1
- CPUS=$2
- if [[ $CGRP = X* ]]
- then
- CGRP=${CGRP#X}
- FILE=cpuset.cpus.exclusive.effective
- else
- FILE=cpuset.cpus.effective
- fi
- [[ $CGRP = A2 ]] && CGRP=A1/A2
- [[ $CGRP = A3 ]] && CGRP=A1/A2/A3
- [[ -e $CGRP/$FILE ]] || return 1
- [[ $CPUS = $(cat $CGRP/$FILE) ]] || return 1
- done
- }
- #
- # Check cgroup states
- # $1 - check string, format: <cgroup>:<state>[,<cgroup>:<state>]*
- #
- check_cgroup_states()
- {
- CHK_STR=$1
- for CHK in $(echo $CHK_STR | sed -e "s/,/ /g")
- do
- set -- $(echo $CHK | sed -e "s/:/ /g")
- CGRP=$1
- CGRP_DIR=$CGRP
- STATE=$2
- FILE=
- EVAL=$(expr substr $STATE 2 2)
- [[ $CGRP = A2 ]] && CGRP_DIR=A1/A2
- [[ $CGRP = A3 ]] && CGRP_DIR=A1/A2/A3
- case $STATE in
- P*) FILE=$CGRP_DIR/cpuset.cpus.partition
- ;;
- *) echo "Unknown state: $STATE!"
- exit 1
- ;;
- esac
- VAL=$(cat $FILE)
- case "$VAL" in
- member) VAL=0
- ;;
- root) VAL=1
- ;;
- isolated)
- VAL=2
- ;;
- "root invalid"*)
- VAL=-1
- ;;
- "isolated invalid"*)
- VAL=-2
- ;;
- esac
- [[ $EVAL != $VAL ]] && return 1
- #
- # 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] 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()
- {
- EXPECT_VAL=$1
- ISOLCPUS=
- LASTISOLCPU=
- SCHED_DOMAINS=/sys/kernel/debug/sched/domains
- ISCPUS=${CGROUP2}/cpuset.cpus.isolated
- if [[ $EXPECT_VAL = . ]]
- then
- EXPECT_VAL=
- EXPECT_VAL2=
- elif [[ $(expr $EXPECT_VAL : ".*,.*") > 0 ]]
- then
- set -- $(echo $EXPECT_VAL | sed -e "s/,/ /g")
- EXPECT_VAL=$1
- EXPECT_VAL2=$2
- else
- EXPECT_VAL2=$EXPECT_VAL
- 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" ]] && {
- EXPECT_VAL=${EXPECT_VAL:+${EXPECT_VAL},}${BOOT_ISOLCPUS}
- EXPECT_VAL2=${EXPECT_VAL2:+${EXPECT_VAL2},}${BOOT_ISOLCPUS}
- }
- #
- # Check cpuset.cpus.isolated cpumask
- #
- [[ "$EXPECT_VAL2" != "$ISOLCPUS" ]] && {
- # Take a 50ms pause and try again
- pause 0.05
- ISOLCPUS=$(cat $ISCPUS)
- }
- [[ "$EXPECT_VAL2" != "$ISOLCPUS" ]] && return 1
- ISOLCPUS=
- #
- # 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
- [[ "$EXPECT_VAL" = "$ISOLCPUS" ]]
- }
- test_fail()
- {
- TESTNUM=$1
- TESTTYPE=$2
- ADDINFO=$3
- echo "Test $TEST[$TESTNUM] failed $TESTTYPE check!"
- [[ -n "$ADDINFO" ]] && echo "*** $ADDINFO ***"
- eval echo \${$TEST[$I]}
- echo
- dump_states
- exit 1
- }
- #
- # 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
- }
- #
- # 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
- 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 ""
- eval echo \${$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}
- set_ctrl_state_noerr B1 $OLD_B1
- set_ctrl_state_noerr A1 $OLD_A1
- set_ctrl_state_noerr A1/A2 $OLD_A2
- set_ctrl_state_noerr A1/A2/A3 $OLD_A3
- RETVAL=0
- set_ctrl_state A1 $NEW_A1; ((RETVAL += $?))
- set_ctrl_state A1/A2 $NEW_A2; ((RETVAL += $?))
- set_ctrl_state A1/A2/A3 $NEW_A3; ((RETVAL += $?))
- set_ctrl_state B1 $NEW_B1; ((RETVAL += $?))
- [[ $RETVAL -ne $RESULT ]] && test_fail $I result
- [[ -n "$ECPUS" && "$ECPUS" != . ]] && {
- check_effective_cpus $ECPUS
- [[ $? -ne 0 ]] && test_fail $I "effective CPU"
- }
- [[ -n "$STATES" && "$STATES" != . ]] && {
- check_cgroup_states $STATES
- [[ $? -ne 0 ]] && test_fail $I states
- }
- # Compare the expected isolated CPUs with the actual ones,
- # if available
- [[ -n "$ICPUS" ]] && {
- check_isolcpus $ICPUS
- [[ $? -ne 0 ]] && {
- [[ -n "$BOOT_ISOLCPUS" ]] && ICPUS=${ICPUS},${BOOT_ISOLCPUS}
- test_fail $I "isolated CPU" \
- "Expect $ICPUS, get $ISOLCPUS instead"
- }
- }
- reset_cgroup_states
- #
- # Check to see if effective cpu list changes
- #
- NEWLIST=$(cat 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 cpuset.cpus.effective)
- done
- [[ $NEWLIST != $CPULIST ]] && {
- echo "Effective cpus changed to $NEWLIST after test $I!"
- exit 1
- }
- null_isolcpus_check
- [[ $VERBOSE -gt 0 ]] && echo "Test $I done."
- ((I++))
- done
- 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 ""
- # Cleaning up
- console_msg "Cleaning up"
- echo $$ > $CGROUP2/cgroup.procs
- [[ -d A1 ]] && rmdir A1
- null_isolcpus_check
- }
- #
- # 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
- $WAIT_INOTIFY $CGROUP_FILE
- cat $CGROUP_FILE > $OUTPUT_FILE
- }
- #
- # 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
- }
- trap cleanup 0 2 3 6
- run_state_test TEST_MATRIX
- test_isolated
- test_inotify
- echo "All tests PASSED."
|