From: Guopeng Zhang <[email protected]> Create an isolated child which owns a boot-isolated CPU, then change its parent's partition type and CPU mask. Verify that a valid child keeps its CPUs and isolation state.
Also test trial parent masks which would invalidate the child because its CPU mask is no longer covered or because it would consume every remaining CPU of a populated parent. Verify that the returning boot-isolated CPU is included in the parent's housekeeping check. Cache the boot-isolated CPU during setup so the tests do not depend on the order in which earlier test cases run. Signed-off-by: Guopeng Zhang <[email protected]> --- .../selftests/cgroup/test_cpuset_prs.sh | 186 +++++++++++++++++- 1 file changed, 182 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh index 9a1cce4807b4..a3df6b094e0f 100755 --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh @@ -107,6 +107,17 @@ then echo "Pre-isolated CPUs: $BOOT_ISOLCPUS" fi +# Cache the CPUs isolated from scheduler domains at boot. +BOOT_ISOLATED_FILE=/sys/devices/system/cpu/isolated +BOOT_CPUS= +BOOT_CPU= +if [[ -r $BOOT_ISOLATED_FILE ]] +then + BOOT_CPUS=$(cat $BOOT_ISOLATED_FILE) + [[ -n "$BOOT_CPUS" ]] && + BOOT_CPU=$(echo "$BOOT_CPUS" | sed -e 's/[,-].*//') +fi + cleanup() { online_cpus @@ -1158,25 +1169,22 @@ test_isolated() } # -# Select an online CPU isolated from scheduler domains at boot. +# Check that a boot-isolated CPU selected during initialization is online. # $1: test name used in the skip message # get_boot_isolated_cpu() { TEST_NAME=$1 - BOOT_ISOLATED_FILE=/sys/devices/system/cpu/isolated [[ -r $BOOT_ISOLATED_FILE ]] || { echo "$TEST_NAME test SKIPPED: boot isolation state unavailable" return 1 } - BOOT_CPUS=$(cat $BOOT_ISOLATED_FILE) [[ -n "$BOOT_CPUS" ]] || { echo "$TEST_NAME test SKIPPED: no boot-isolated CPU" return 1 } - BOOT_CPU=$(echo "$BOOT_CPUS" | sed -e 's/[,-].*//') CPU_ONLINE=/sys/devices/system/cpu/cpu${BOOT_CPU}/online [[ ! -e $CPU_ONLINE || $(cat $CPU_ONLINE) -eq 1 ]] || { echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is offline" @@ -1214,6 +1222,174 @@ test_boot_isolated() echo "$TEST_NAME test PASSED." } +# +# A parent's type and CPU-mask changes must check only CPUs owned directly by +# the parent, not a boot-isolated CPU owned by a valid child partition. +# +test_child_owned_cpus() +{ + TEST_NAME="Child-owned CPU type change" + get_boot_isolated_cpu "$TEST_NAME" || return 0 + echo "Running $TEST_NAME test ..." + + cd $CGROUP2/test + echo member > cpuset.cpus.partition + echo +cpuset > cgroup.subtree_control + echo 2,$BOOT_CPU > cpuset.cpus + [[ $(cat cpuset.cpus.effective) = "2,$BOOT_CPU" ]] || { + echo "$TEST_NAME test SKIPPED: CPUs 2,$BOOT_CPU are unavailable" + echo "" > cpuset.cpus + cd $CGROUP2 + return 0 + } + test_partition isolated + mkdir A1 + cd A1 + echo $BOOT_CPU > cpuset.cpus + test_partition isolated + cd .. + test_effective_cpus 2 + test_partition root + echo 2-3,$BOOT_CPU > cpuset.cpus + test_effective_cpus 2-3 + [[ $(cat cpuset.cpus.partition) = root ]] || { + echo "Parent partition became invalid during CPU update" + exit 1 + } + [[ $(cat A1/cpuset.cpus.partition) = isolated ]] || { + echo "Child partition changed during parent update" + exit 1 + } + check_isolcpus "." || { + echo "Parent update corrupted child isolation" + exit 1 + } + cd A1 + test_partition member + cd .. + rmdir A1 + test_partition member + echo "" > cpuset.cpus + cd $CGROUP2 + echo "$TEST_NAME test PASSED." +} + +# +# A child that will become invalid under a trial parent CPU mask no longer +# owns its CPUs for the purpose of the parent's housekeeping validation. +# +test_trial_child_invalidation() +{ + TEST_NAME="Trial child partition invalidation" + get_boot_isolated_cpu "$TEST_NAME" || return 0 + echo "Running $TEST_NAME test ..." + + cd $CGROUP2/test + echo member > cpuset.cpus.partition + echo +cpuset > cgroup.subtree_control + echo 2-3,$BOOT_CPU > cpuset.cpus + [[ $(cat cpuset.cpus.effective) = "2-3,$BOOT_CPU" ]] || { + echo "$TEST_NAME test SKIPPED: CPUs 2-3,$BOOT_CPU are unavailable" + echo "" > cpuset.cpus + cd $CGROUP2 + return 0 + } + + # + # The child would fail validation with PERR_INVCPUS because CPU 2 is + # absent from the trial parent mask. CPU $BOOT_CPU must therefore be + # checked as a CPU that will return to the parent, not subtracted as + # child-owned. + # + test_partition isolated + mkdir A1 + cd A1 + echo 2,$BOOT_CPU > cpuset.cpus + test_partition isolated + cd .. + test_partition root + test_effective_cpus 3 + echo 3,$BOOT_CPU > cpuset.cpus + grep -q '^root invalid (partition config conflicts with housekeeping setup)$' \ + cpuset.cpus.partition || { + echo "PERR_INVCPUS trial allowed the parent to bypass housekeeping validation" + exit 1 + } + [[ $(cat A1/cpuset.cpus.partition) = "isolated invalid"* ]] || { + echo "Child did not become invalid after the parent CPU update" + exit 1 + } + [[ $(cat $CGROUP2/cpuset.cpus.effective) = "$CPULIST" ]] || { + echo "PERR_INVCPUS trial did not release the parent partition CPUs" + exit 1 + } + # + # An invalid isolated child retains its isolation state after its + # effective_xcpus is cleared. Explicitly transition CPU 2 through an + # isolated partition before starting the next case. + # + echo member > A1/cpuset.cpus.partition + rmdir A1 + echo member > cpuset.cpus.partition + echo "" > cpuset.cpus + echo 2 > cpuset.cpus + test_partition isolated + test_partition member + echo "" > cpuset.cpus + check_isolcpus "." || { + echo "PERR_INVCPUS trial cleanup left CPU 2 isolated" + exit 1 + } + + # + # With a task in the parent, the child would fail validation with + # PERR_NOCPUS because it consumes all CPUs in the trial mask. Its + # boot-isolated CPU must therefore participate in the parent's + # housekeeping validation. + # + echo 2-3,$BOOT_CPU > cpuset.cpus + test_partition isolated + mkdir A1 + cd A1 + echo 2,$BOOT_CPU > cpuset.cpus + test_partition isolated + cd .. + test_partition root + test_effective_cpus 3 + echo 0 > cgroup.procs + echo 2,$BOOT_CPU > cpuset.cpus + echo 0 > $CGROUP2/cgroup.procs + grep -q '^root invalid (partition config conflicts with housekeeping setup)$' \ + cpuset.cpus.partition || { + echo "PERR_NOCPUS trial allowed the parent to bypass housekeeping validation" + exit 1 + } + [[ $(cat A1/cpuset.cpus.partition) = "isolated invalid"* ]] || { + echo "Child did not become invalid after exhausting the parent CPUs" + exit 1 + } + [[ $(cat $CGROUP2/cpuset.cpus.effective) = "$CPULIST" ]] || { + echo "PERR_NOCPUS trial did not release the parent partition CPUs" + exit 1 + } + # Clean up the retained isolation state as in the PERR_INVCPUS case. + echo member > A1/cpuset.cpus.partition + rmdir A1 + echo member > cpuset.cpus.partition + echo "" > cpuset.cpus + echo 2 > cpuset.cpus + test_partition isolated + test_partition member + echo "" > cpuset.cpus + check_isolcpus "." || { + echo "PERR_NOCPUS trial cleanup left CPU 2 isolated" + exit 1 + } + + cd $CGROUP2 + echo "$TEST_NAME test PASSED." +} + # # Wait for inotify event for the given file and read it # $1: cgroup file to wait for @@ -1286,5 +1462,7 @@ run_state_test TEST_MATRIX run_remote_state_test REMOTE_TEST_MATRIX test_isolated test_boot_isolated +test_child_owned_cpus +test_trial_child_invalidation test_inotify echo "All tests PASSED." -- 2.43.0

