|  | #!/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. 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 | 
|  |  | 
|  | cleanup() | 
|  | { | 
|  | online_cpus | 
|  | cd $CGROUP2 | 
|  | rmdir A1/A2/A3 A1/A2 A1 B1 test/A1 test/B1 test > /dev/null 2>&1 | 
|  | rmdir rtest/p1/c11 rtest/p1/c12 rtest/p2/c21 \ | 
|  | rtest/p2/c22 rtest/p1 rtest/p2 rtest > /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            B1 | 
|  | #	 | | 
|  | #	 A2 | 
|  | #	 | | 
|  | #	 A3 | 
|  | # | 
|  | #  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 | 
|  | #  CX<l> = add cpu-list to both cpuset.cpus and cpuset.cpus.exclusive | 
|  | #  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|XA2:2-3" | 
|  | "C0-3:P1:S+ C2-3:P1 .      .     C1-3    .      .      .     0 A1:1|A2:2-3|XA2:2-3" | 
|  | "C2-3:P1:S+  C3:P1  .      .     C3      .      .      .     0 A1:|A2:3|XA2: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" | 
|  |  | 
|  | # 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-2|A2:1-2|XA2:3|XA3:3|A3:3|XA3:3 \ | 
|  | A1:P0|A3:P2 3" | 
|  | " 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 0-1|1" | 
|  |  | 
|  | # 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" | 
|  |  | 
|  | # 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" | 
|  |  | 
|  | #  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" | 
|  | ) | 
|  |  | 
|  | # | 
|  | # Cpuset controller remote partition test matrix. | 
|  | # | 
|  | # Cgroup test hierarchy | 
|  | # | 
|  | #	      root | 
|  | #	        | | 
|  | #	      rtest (cpuset.cpus.exclusive=1-7) | 
|  | #	        | | 
|  | #	 +------+------+ | 
|  | #	 |             | | 
|  | #	 p1            p2 | 
|  | #     +--+--+       +--+--+ | 
|  | #     |     |       |     | | 
|  | #    c11   c12     c21   c22 | 
|  | # | 
|  | # REMOTE_TEST_MATRIX uses the same notational convention as TEST_MATRIX. | 
|  | # Only CPUs 1-7 should be used. | 
|  | # | 
|  | REMOTE_TEST_MATRIX=( | 
|  | #  old-p1 old-p2 old-c11 old-c12 old-c21 old-c22 | 
|  | #  new-p1 new-p2 new-c11 new-c12 new-c21 new-c22 ECPUs Pstate ISOLCPUS | 
|  | #  ------ ------ ------- ------- ------- ------- ----- ------ -------- | 
|  | " X1-3:S+ X4-6:S+ X1-2     X3     X4-5     X6 \ | 
|  | .      .     P2      P2      P2      P2    c11:1-2|c12:3|c21:4-5|c22:6 \ | 
|  | c11:P2|c12:P2|c21:P2|c22:P2 1-6" | 
|  | " CX1-4:S+   .   X1-2:P2   C3      .       .  \ | 
|  | .      .     .      C3-4     .       .     p1:3-4|c11:1-2|c12:3-4 \ | 
|  | p1:P0|c11:P2|c12:P0 1-2" | 
|  | " CX1-4:S+   .   X1-2:P2   .       .       .  \ | 
|  | X2-4     .     .       .       .       .     p1:1,3-4|c11:2 \ | 
|  | p1:P0|c11:P2 2" | 
|  | " CX1-5:S+   .   X1-2:P2 X3-5:P1   .       .  \ | 
|  | X2-4     .     .       .       .       .     p1:1,5|c11:2|c12:3-4 \ | 
|  | p1:P0|c11:P2|c12:P1 2" | 
|  | " CX1-4:S+   .   X1-2:P2 X3-4:P1   .       .  \ | 
|  | .      .     X2      .       .       .     p1:1|c11:2|c12:3-4 \ | 
|  | p1:P0|c11:P2|c12:P1 2" | 
|  | # p1 as member, will get its effective CPUs from its parent rtest | 
|  | " CX1-4:S+   .   X1-2:P2 X3-4:P1   .       .  \ | 
|  | .      .     X1     CX2-4    .       .     p1:5-7|c11:1|c12:2-4 \ | 
|  | p1:P0|c11:P2|c12:P1 1" | 
|  | " CX1-4:S+ X5-6:P1:S+ .    .       .       .  \ | 
|  | .      .   X1-2:P2  X4-5:P1  .     X1-7:P2 p1:3|c11:1-2|c12:4:c22:5-6 \ | 
|  | p1:P0|p2:P1|c11:P2|c12:P1|c22:P2 \ | 
|  | 1-2,4-6|1-2,5-6" | 
|  | ) | 
|  |  | 
|  | # | 
|  | # 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 | 
|  | case $CMD in | 
|  | S*) PREFIX=${CMD#?} | 
|  | COMM="echo ${PREFIX}${CTRL} > $SFILE" | 
|  | eval $COMM $REDIRECT | 
|  | ;; | 
|  | X*) | 
|  | CPUS=${CMD#?} | 
|  | COMM="echo $CPUS > $XFILE" | 
|  | eval $COMM $REDIRECT | 
|  | ;; | 
|  | CX*) | 
|  | CPUS=${CMD#??} | 
|  | COMM="echo $CPUS > $CFILE; echo $CPUS > $XFILE" | 
|  | eval $COMM $REDIRECT | 
|  | ;; | 
|  | C*) CPUS=${CMD#?} | 
|  | COMM="echo $CPUS > $CFILE" | 
|  | eval $COMM $REDIRECT | 
|  | ;; | 
|  | P*) 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 | 
|  | ;; | 
|  | O*) VAL=${CMD#?} | 
|  | write_cpu_online $VAL | 
|  | ;; | 
|  | T*) COMM="echo 0 > $TFILE" | 
|  | eval $COMM $REDIRECT | 
|  | ;; | 
|  | *)  echo "Unknown command: $CMD" | 
|  | exit 1 | 
|  | ;; | 
|  | esac | 
|  | 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 | 
|  | } | 
|  | } | 
|  |  | 
|  | # | 
|  | # Remove all the test cgroup directories | 
|  | # | 
|  | reset_cgroup_states() | 
|  | { | 
|  | echo 0 > $CGROUP2/cgroup.procs | 
|  | online_cpus | 
|  | rmdir $RESET_LIST > /dev/null 2>&1 | 
|  | } | 
|  |  | 
|  | dump_states() | 
|  | { | 
|  | for DIR in $CGROUP_LIST | 
|  | 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 | 
|  | } | 
|  |  | 
|  | # | 
|  | # Set the actual cgroup directory into $CGRP_DIR | 
|  | # $1 - cgroup name | 
|  | # | 
|  | set_cgroup_dir() | 
|  | { | 
|  | CGRP_DIR=$1 | 
|  | [[ $CGRP_DIR = A2  ]] && CGRP_DIR=A1/A2 | 
|  | [[ $CGRP_DIR = A3  ]] && CGRP_DIR=A1/A2/A3 | 
|  | [[ $CGRP_DIR = c11 ]] && CGRP_DIR=p1/c11 | 
|  | [[ $CGRP_DIR = c12 ]] && CGRP_DIR=p1/c12 | 
|  | [[ $CGRP_DIR = c21 ]] && CGRP_DIR=p2/c21 | 
|  | [[ $CGRP_DIR = c22 ]] && CGRP_DIR=p2/c22 | 
|  | } | 
|  |  | 
|  | # | 
|  | # 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 | 
|  | EXPECTED_CPUS=$2 | 
|  | ACTUAL_CPUS= | 
|  | if [[ $CGRP = X* ]] | 
|  | then | 
|  | CGRP=${CGRP#X} | 
|  | FILE=cpuset.cpus.exclusive.effective | 
|  | else | 
|  | FILE=cpuset.cpus.effective | 
|  | fi | 
|  | set_cgroup_dir $CGRP | 
|  | [[ -e $CGRP_DIR/$FILE ]] || return 1 | 
|  | ACTUAL_CPUS=$(cat $CGRP_DIR/$FILE) | 
|  | [[ $EXPECTED_CPUS = $ACTUAL_CPUS ]] || 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 | 
|  | EXPECTED_STATE=$2 | 
|  | FILE= | 
|  | EVAL=$(expr substr $EXPECTED_STATE 2 2) | 
|  |  | 
|  | set_cgroup_dir $CGRP | 
|  | case $EXPECTED_STATE in | 
|  | P*) FILE=$CGRP_DIR/cpuset.cpus.partition | 
|  | ;; | 
|  | *)  echo "Unknown state: $EXPECTED_STATE!" | 
|  | exit 1 | 
|  | ;; | 
|  | esac | 
|  | ACTUAL_STATE=$(cat $FILE) | 
|  |  | 
|  | case "$ACTUAL_STATE" 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_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 | 
|  |  | 
|  | [[ "$EXPECTED_SDOMAIN" = "$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 | 
|  | } | 
|  |  | 
|  | # | 
|  | # 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" | 
|  |  | 
|  | [[ -n "$_ECPUS" && "$_ECPUS" != . ]] && { | 
|  | check_effective_cpus $_ECPUS | 
|  | [[ $? -ne 0 ]] && test_fail $_NR "effective CPU" \ | 
|  | "Cgroup $CGRP: expected $EXPECTED_CPUS, got $ACTUAL_CPUS" | 
|  | } | 
|  |  | 
|  | [[ -n "$_PSTATES" && "$_PSTATES" != . ]] && { | 
|  | check_cgroup_states $_PSTATES | 
|  | [[ $? -ne 0 ]] && test_fail $_NR states \ | 
|  | "Cgroup $CGRP: expected $EXPECTED_STATE, got $ACTUAL_STATE" | 
|  | } | 
|  |  | 
|  | # 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 "" | 
|  | 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 A1       $OLD_A1 | 
|  | set_ctrl_state_noerr A1/A2    $OLD_A2 | 
|  | set_ctrl_state_noerr A1/A2/A3 $OLD_A3 | 
|  | set_ctrl_state_noerr B1       $OLD_B1 | 
|  |  | 
|  | 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 | 
|  |  | 
|  | check_test_results $I "$ECPUS" "$STATES" "$ICPUS" | 
|  | ((I++)) | 
|  | done | 
|  | echo "All $I tests of $TEST PASSED." | 
|  | } | 
|  |  | 
|  | # | 
|  | # Run cpuset remote partition state transition test | 
|  | #  $1 - test matrix name | 
|  | # | 
|  | run_remote_state_test() | 
|  | { | 
|  | TEST=$1 | 
|  | CONTROLLER=cpuset | 
|  | [[ -d rtest ]] || mkdir rtest | 
|  | cd rtest | 
|  | echo +cpuset > cgroup.subtree_control | 
|  | echo "1-7" > cpuset.cpus | 
|  | echo "1-7" > cpuset.cpus.exclusive | 
|  | CGROUP_LIST=".. . p1 p2 p1/c11 p1/c12 p2/c21 p2/c22" | 
|  | RESET_LIST="p1/c11 p1/c12 p2/c21 p2/c22 p1 p2" | 
|  | I=0 | 
|  | eval CNT="\${#$TEST[@]}" | 
|  |  | 
|  | reset_cgroup_states | 
|  | console_msg "Running remote partition 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_p1=$1 | 
|  | OLD_p2=$2 | 
|  | OLD_c11=$3 | 
|  | OLD_c12=$4 | 
|  | OLD_c21=$5 | 
|  | OLD_c22=$6 | 
|  | NEW_p1=$7 | 
|  | NEW_p2=$8 | 
|  | NEW_c11=$9 | 
|  | NEW_c12=${10} | 
|  | NEW_c21=${11} | 
|  | NEW_c22=${12} | 
|  | ECPUS=${13} | 
|  | STATES=${14} | 
|  | ICPUS=${15} | 
|  |  | 
|  | set_ctrl_state_noerr p1     $OLD_p1 | 
|  | set_ctrl_state_noerr p2     $OLD_p2 | 
|  | set_ctrl_state_noerr p1/c11 $OLD_c11 | 
|  | set_ctrl_state_noerr p1/c12 $OLD_c12 | 
|  | set_ctrl_state_noerr p2/c21 $OLD_c21 | 
|  | set_ctrl_state_noerr p2/c22 $OLD_c22 | 
|  |  | 
|  | RETVAL=0 | 
|  | set_ctrl_state p1     $NEW_p1 ; ((RETVAL += $?)) | 
|  | set_ctrl_state p2     $NEW_p2 ; ((RETVAL += $?)) | 
|  | set_ctrl_state p1/c11 $NEW_c11; ((RETVAL += $?)) | 
|  | set_ctrl_state p1/c12 $NEW_c12; ((RETVAL += $?)) | 
|  | set_ctrl_state p2/c21 $NEW_c21; ((RETVAL += $?)) | 
|  | set_ctrl_state p2/c22 $NEW_c22; ((RETVAL += $?)) | 
|  |  | 
|  | [[ $RETVAL -ne 0 ]] && test_fail $I result | 
|  |  | 
|  | 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 "" | 
|  |  | 
|  | # Cleaning up | 
|  | console_msg "Cleaning up" | 
|  | echo $$ > $CGROUP2/cgroup.procs | 
|  | [[ -d A1 ]] && rmdir A1 | 
|  | null_isolcpus_check | 
|  | pause 0.05 | 
|  | } | 
|  |  | 
|  | # | 
|  | # 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 | 
|  | echo member > cpuset.cpus.partition | 
|  | echo "" > cpuset.cpus | 
|  | } | 
|  |  | 
|  | trap cleanup 0 2 3 6 | 
|  | run_state_test TEST_MATRIX | 
|  | run_remote_state_test REMOTE_TEST_MATRIX | 
|  | test_isolated | 
|  | test_inotify | 
|  | echo "All tests PASSED." |