1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# 4# Test for cpuset v2 partition root state (PRS) 5# 6# The sched verbose flag can be optionally set so that the console log 7# can be examined for the correct setting of scheduling domain. 8# 9 10skip_test() { 11 echo "$1" 12 echo "Test SKIPPED" 13 exit 4 # ksft_skip 14} 15 16[[ $(id -u) -eq 0 ]] || skip_test "Test must be run as root!" 17 18 19# Get wait_inotify location 20WAIT_INOTIFY=$(cd $(dirname $0); pwd)/wait_inotify 21 22# Find cgroup v2 mount point 23CGROUP2=$(mount -t cgroup2 | head -1 | awk -e '{print $3}') 24[[ -n "$CGROUP2" ]] || skip_test "Cgroup v2 mount point not found!" 25SUBPARTS_CPUS=$CGROUP2/.__DEBUG__.cpuset.cpus.subpartitions 26CPULIST=$(cat $CGROUP2/cpuset.cpus.effective) 27 28NR_CPUS=$(lscpu | grep "^CPU(s):" | sed -e "s/.*:[[:space:]]*//") 29[[ $NR_CPUS -lt 8 ]] && skip_test "Test needs at least 8 cpus available!" 30 31# Set verbose flag and delay factor 32PROG=$1 33VERBOSE=0 34DELAY_FACTOR=1 35SCHED_DEBUG= 36while [[ "$1" = -* ]] 37do 38 case "$1" in 39 -v) ((VERBOSE++)) 40 # Enable sched/verbose can slow thing down 41 [[ $DELAY_FACTOR -eq 1 ]] && 42 DELAY_FACTOR=2 43 ;; 44 -d) DELAY_FACTOR=$2 45 shift 46 ;; 47 *) echo "Usage: $PROG [-v] [-d <delay-factor>" 48 exit 49 ;; 50 esac 51 shift 52done 53 54# Set sched verbose flag if available when "-v" option is specified 55if [[ $VERBOSE -gt 0 && -d /sys/kernel/debug/sched ]] 56then 57 # Used to restore the original setting during cleanup 58 SCHED_DEBUG=$(cat /sys/kernel/debug/sched/verbose) 59 echo Y > /sys/kernel/debug/sched/verbose 60fi 61 62cd $CGROUP2 63echo +cpuset > cgroup.subtree_control 64 65# 66# If cpuset has been set up and used in child cgroups, we may not be able to 67# create partition under root cgroup because of the CPU exclusivity rule. 68# So we are going to skip the test if this is the case. 69# 70[[ -d test ]] || mkdir test 71echo 0-6 > test/cpuset.cpus 72echo root > test/cpuset.cpus.partition 73cat test/cpuset.cpus.partition | grep -q invalid 74RESULT=$? 75echo member > test/cpuset.cpus.partition 76echo "" > test/cpuset.cpus 77[[ $RESULT -eq 0 ]] && skip_test "Child cgroups are using cpuset!" 78 79cleanup() 80{ 81 online_cpus 82 cd $CGROUP2 83 rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1 84 rmdir test > /dev/null 2>&1 85 [[ -n "$SCHED_DEBUG" ]] && 86 echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose 87} 88 89# Pause in ms 90pause() 91{ 92 DELAY=$1 93 LOOP=0 94 while [[ $LOOP -lt $DELAY_FACTOR ]] 95 do 96 sleep $DELAY 97 ((LOOP++)) 98 done 99 return 0 100} 101 102console_msg() 103{ 104 MSG=$1 105 echo "$MSG" 106 echo "" > /dev/console 107 echo "$MSG" > /dev/console 108 pause 0.01 109} 110 111test_partition() 112{ 113 EXPECTED_VAL=$1 114 echo $EXPECTED_VAL > cpuset.cpus.partition 115 [[ $? -eq 0 ]] || exit 1 116 ACTUAL_VAL=$(cat cpuset.cpus.partition) 117 [[ $ACTUAL_VAL != $EXPECTED_VAL ]] && { 118 echo "cpuset.cpus.partition: expect $EXPECTED_VAL, found $ACTUAL_VAL" 119 echo "Test FAILED" 120 exit 1 121 } 122} 123 124test_effective_cpus() 125{ 126 EXPECTED_VAL=$1 127 ACTUAL_VAL=$(cat cpuset.cpus.effective) 128 [[ "$ACTUAL_VAL" != "$EXPECTED_VAL" ]] && { 129 echo "cpuset.cpus.effective: expect '$EXPECTED_VAL', found '$ACTUAL_VAL'" 130 echo "Test FAILED" 131 exit 1 132 } 133} 134 135# Adding current process to cgroup.procs as a test 136test_add_proc() 137{ 138 OUTSTR="$1" 139 ERRMSG=$((echo $$ > cgroup.procs) |& cat) 140 echo $ERRMSG | grep -q "$OUTSTR" 141 [[ $? -ne 0 ]] && { 142 echo "cgroup.procs: expect '$OUTSTR', got '$ERRMSG'" 143 echo "Test FAILED" 144 exit 1 145 } 146 echo $$ > $CGROUP2/cgroup.procs # Move out the task 147} 148 149# 150# Cpuset controller state transition test matrix. 151# 152# Cgroup test hierarchy 153# 154# root -- A1 -- A2 -- A3 155# +- B1 156# 157# P<v> = set cpus.partition (0:member, 1:root, 2:isolated) 158# C<l> = add cpu-list to cpuset.cpus 159# X<l> = add cpu-list to cpuset.cpus.exclusive 160# S<p> = use prefix in subtree_control 161# T = put a task into cgroup 162# O<c>=<v> = Write <v> to CPU online file of <c> 163# 164SETUP_A123_PARTITIONS="C1-3:P1:S+ C2-3:P1:S+ C3:P1" 165TEST_MATRIX=( 166 # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS 167 # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ -------- 168 " C0-1 . . C2-3 S+ C4-5 . . 0 A2:0-1" 169 " C0-1 . . C2-3 P1 . . . 0 " 170 " C0-1 . . C2-3 P1:S+ C0-1:P1 . . 0 " 171 " C0-1 . . C2-3 P1:S+ C1:P1 . . 0 " 172 " C0-1:S+ . . C2-3 . . . P1 0 " 173 " C0-1:P1 . . C2-3 S+ C1 . . 0 " 174 " C0-1:P1 . . C2-3 S+ C1:P1 . . 0 " 175 " C0-1:P1 . . C2-3 S+ C1:P1 . P1 0 " 176 " C0-1:P1 . . C2-3 C4-5 . . . 0 A1:4-5" 177 " C0-1:P1 . . C2-3 S+:C4-5 . . . 0 A1:4-5" 178 " C0-1 . . C2-3:P1 . . . C2 0 " 179 " C0-1 . . C2-3:P1 . . . C4-5 0 B1:4-5" 180 "C0-3:P1:S+ C2-3:P1 . . . . . . 0 A1:0-1,A2:2-3" 181 "C0-3:P1:S+ C2-3:P1 . . C1-3 . . . 0 A1:1,A2:2-3" 182 "C2-3:P1:S+ C3:P1 . . C3 . . . 0 A1:,A2:3 A1:P1,A2:P1" 183 "C2-3:P1:S+ C3:P1 . . C3 P0 . . 0 A1:3,A2:3 A1:P1,A2:P0" 184 "C2-3:P1:S+ C2:P1 . . C2-4 . . . 0 A1:3-4,A2:2" 185 "C2-3:P1:S+ C3:P1 . . C3 . . C0-2 0 A1:,B1:0-2 A1:P1,A2:P1" 186 "$SETUP_A123_PARTITIONS . C2-3 . . . 0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1" 187 188 # CPU offlining cases: 189 " C0-1 . . C2-3 S+ C4-5 . O2=0 0 A1:0-1,B1:3" 190 "C0-3:P1:S+ C2-3:P1 . . O2=0 . . . 0 A1:0-1,A2:3" 191 "C0-3:P1:S+ C2-3:P1 . . O2=0 O2=1 . . 0 A1:0-1,A2:2-3" 192 "C0-3:P1:S+ C2-3:P1 . . O1=0 . . . 0 A1:0,A2:2-3" 193 "C0-3:P1:S+ C2-3:P1 . . O1=0 O1=1 . . 0 A1:0-1,A2:2-3" 194 "C2-3:P1:S+ C3:P1 . . O3=0 O3=1 . . 0 A1:2,A2:3 A1:P1,A2:P1" 195 "C2-3:P1:S+ C3:P2 . . O3=0 O3=1 . . 0 A1:2,A2:3 A1:P1,A2:P2" 196 "C2-3:P1:S+ C3:P1 . . O2=0 O2=1 . . 0 A1:2,A2:3 A1:P1,A2:P1" 197 "C2-3:P1:S+ C3:P2 . . O2=0 O2=1 . . 0 A1:2,A2:3 A1:P1,A2:P2" 198 "C2-3:P1:S+ C3:P1 . . O2=0 . . . 0 A1:,A2:3 A1:P1,A2:P1" 199 "C2-3:P1:S+ C3:P1 . . O3=0 . . . 0 A1:2,A2: A1:P1,A2:P1" 200 "C2-3:P1:S+ C3:P1 . . T:O2=0 . . . 0 A1:3,A2:3 A1:P1,A2:P-1" 201 "C2-3:P1:S+ C3:P1 . . . T:O3=0 . . 0 A1:2,A2:2 A1:P1,A2:P-1" 202 "$SETUP_A123_PARTITIONS . O1=0 . . . 0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1" 203 "$SETUP_A123_PARTITIONS . O2=0 . . . 0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1" 204 "$SETUP_A123_PARTITIONS . O3=0 . . . 0 A1:1,A2:2,A3: A1:P1,A2:P1,A3:P1" 205 "$SETUP_A123_PARTITIONS . T:O1=0 . . . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1" 206 "$SETUP_A123_PARTITIONS . . T:O2=0 . . 0 A1:1,A2:3,A3:3 A1:P1,A2:P1,A3:P-1" 207 "$SETUP_A123_PARTITIONS . . . T:O3=0 . 0 A1:1,A2:2,A3:2 A1:P1,A2:P1,A3:P-1" 208 "$SETUP_A123_PARTITIONS . T:O1=0 O1=1 . . 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1" 209 "$SETUP_A123_PARTITIONS . . T:O2=0 O2=1 . 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1" 210 "$SETUP_A123_PARTITIONS . . . T:O3=0 O3=1 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1" 211 "$SETUP_A123_PARTITIONS . T:O1=0 O2=0 O1=1 . 0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1" 212 "$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" 213 214 # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS 215 # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ -------- 216 # 217 # Remote partition and cpuset.cpus.exclusive tests 218 # 219 " C0-3:S+ C1-3:S+ C2-3 . X2-3 . . . 0 A1:0-3,A2:1-3,A3:2-3,XA1:2-3" 220 " 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" 221 " 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" 222 " 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" 223 " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:C3 . 0 A1:0-2,A2:1-2,A3:3 A1:P0,A3:P2 3" 224 " 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" 225 " C0-3:S+ C1-3:S+ C2-3 C4-5 . . . P2 0 B1:4-5 B1:P2 4-5" 226 " 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" 227 " 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" 228 " 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" 229 " 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" 230 231 # Nested remote/local partition tests 232 " 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 \ 233 A1:P0,A2:P1,A3:P2,B1:P1 2-3" 234 " 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 \ 235 A1:P0,A2:P1,A3:P2,B1:P1 2-4,2-3" 236 " 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 \ 237 A1:P0,A2:P1,A3:P2,B1:P1 2-4,3" 238 " C0-4:S+ C1-4:S+ C2-4 . X2-4 X2-4:P2 X4:P1 . 0 A1:0-1,A2:2-3,A3:4 \ 239 A1:P0,A2:P2,A3:P1 2-4,2-3" 240 " C0-4:X2-4:S+ C1-4:X2-4:S+:P2 C2-4:X4:P1 \ 241 . . X5 . . 0 A1:0-4,A2:1-4,A3:2-4 \ 242 A1:P0,A2:P-2,A3:P-1" 243 " C0-4:X2-4:S+ C1-4:X2-4:S+:P2 C2-4:X4:P1 \ 244 . . . X1 . 0 A1:0-1,A2:2-4,A3:2-4 \ 245 A1:P0,A2:P2,A3:P-1 2-4" 246 247 # Remote partition offline tests 248 " 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" 249 " 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" 250 " 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" 251 " 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," 252 253 # An invalidated remote partition cannot self-recover from hotplug 254 " 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" 255 256 # cpus.exclusive.effective clearing test 257 " C0-3:S+ C1-3:S+ C2 . X2-3:X . . . 0 A1:0-3,A2:1-3,A3:2,XA1:" 258 259 # Invalid to valid remote partition transition test 260 " C0-3:S+ C1-3 . . . X3:P2 . . 0 A1:0-3,A2:1-3,XA2: A2:P-2" 261 " C0-3:S+ C1-3:X3:P2 262 . . X2-3 P2 . . 0 A1:0-2,A2:3,XA2:3 A2:P2 3" 263 264 # Invalid to valid local partition direct transition tests 265 " C1-3:S+:P2 C2-3:X1:P2 . . . . . . 0 A1:1-3,XA1:1-3,A2:2-3:XA2: A1:P2,A2:P-2 1-3" 266 " C1-3:S+:P2 C2-3:X1:P2 . . . X3:P2 . . 0 A1:1-2,XA1:1-3,A2:3:XA2:3 A1:P2,A2:P2 1-3" 267 " C0-3:P2 . . C4-6 C0-4 . . . 0 A1:0-4,B1:4-6 A1:P-2,B1:P0" 268 " C0-3:P2 . . C4-6 C0-4:C0-3 . . . 0 A1:0-3,B1:4-6 A1:P2,B1:P0 0-3" 269 " C0-3:P2 . . C3-5:C4-5 . . . . 0 A1:0-3,B1:4-5 A1:P2,B1:P0 0-3" 270 271 # Local partition invalidation tests 272 " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \ 273 . . . . . 0 A1:1,A2:2,A3:3 A1:P2,A2:P2,A3:P2 1-3" 274 " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \ 275 . . X4 . . 0 A1:1-3,A2:1-3,A3:2-3,XA2:,XA3: A1:P2,A2:P-2,A3:P-2 1-3" 276 " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \ 277 . . C4 . . 0 A1:1-3,A2:1-3,A3:2-3,XA2:,XA3: A1:P2,A2:P-2,A3:P-2 1-3" 278 # Local partition CPU change tests 279 " C0-5:S+:P2 C4-5:S+:P1 . . . C3-5 . . 0 A1:0-2,A2:3-5 A1:P2,A2:P1 0-2" 280 " C0-5:S+:P2 C4-5:S+:P1 . . C1-5 . . . 0 A1:1-3,A2:4-5 A1:P2,A2:P1 1-3" 281 282 # cpus_allowed/exclusive_cpus update tests 283 " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \ 284 . C4 . P2 . 0 A1:4,A2:4,XA2:,XA3:,A3:4 \ 285 A1:P0,A3:P-2" 286 " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \ 287 . X1 . P2 . 0 A1:0-3,A2:1-3,XA1:1,XA2:,XA3:,A3:2-3 \ 288 A1:P0,A3:P-2" 289 " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \ 290 . . C3 P2 . 0 A1:0-2,A2:0-2,XA2:3,XA3:3,A3:3 \ 291 A1:P0,A3:P2 3" 292 " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \ 293 . . X3 P2 . 0 A1:0-2,A2:1-2,XA2:3,XA3:3,A3:3 \ 294 A1:P0,A3:P2 3" 295 " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \ 296 . . X3 . . 0 A1:0-3,A2:1-3,XA2:3,XA3:3,A3:2-3 \ 297 A1:P0,A3:P-2" 298 " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \ 299 . . C3 . . 0 A1:0-3,A2:3,XA2:3,XA3:3,A3:3 \ 300 A1:P0,A3:P-2" 301 " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \ 302 . C4 . . . 0 A1:4,A2:4,A3:4,XA1:,XA2:,XA3 \ 303 A1:P0,A3:P-2" 304 305 # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS 306 # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ -------- 307 # 308 # Incorrect change to cpuset.cpus invalidates partition root 309 # 310 # Adding CPUs to partition root that are not in parent's 311 # cpuset.cpus is allowed, but those extra CPUs are ignored. 312 "C2-3:P1:S+ C3:P1 . . . C2-4 . . 0 A1:,A2:2-3 A1:P1,A2:P1" 313 314 # Taking away all CPUs from parent or itself if there are tasks 315 # will make the partition invalid. 316 "C2-3:P1:S+ C3:P1 . . T C2-3 . . 0 A1:2-3,A2:2-3 A1:P1,A2:P-1" 317 " C3:P1:S+ C3 . . T P1 . . 0 A1:3,A2:3 A1:P1,A2:P-1" 318 "$SETUP_A123_PARTITIONS . T:C2-3 . . . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1" 319 "$SETUP_A123_PARTITIONS . T:C2-3:C1-3 . . . 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1" 320 321 # Changing a partition root to member makes child partitions invalid 322 "C2-3:P1:S+ C3:P1 . . P0 . . . 0 A1:2-3,A2:3 A1:P0,A2:P-1" 323 "$SETUP_A123_PARTITIONS . C2-3 P0 . . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P0,A3:P-1" 324 325 # cpuset.cpus can contains cpus not in parent's cpuset.cpus as long 326 # as they overlap. 327 "C2-3:P1:S+ . . . . C3-4:P1 . . 0 A1:2,A2:3 A1:P1,A2:P1" 328 329 # Deletion of CPUs distributed to child cgroup is allowed. 330 "C0-1:P1:S+ C1 . C2-3 C4-5 . . . 0 A1:4-5,A2:4-5" 331 332 # To become a valid partition root, cpuset.cpus must overlap parent's 333 # cpuset.cpus. 334 " C0-1:P1 . . C2-3 S+ C4-5:P1 . . 0 A1:0-1,A2:0-1 A1:P1,A2:P-1" 335 336 # Enabling partition with child cpusets is allowed 337 " C0-1:S+ C1 . C2-3 P1 . . . 0 A1:0-1,A2:1 A1:P1" 338 339 # A partition root with non-partition root parent is invalid, but it 340 # can be made valid if its parent becomes a partition root too. 341 " C0-1:S+ C1 . C2-3 . P2 . . 0 A1:0-1,A2:1 A1:P0,A2:P-2" 342 " C0-1:S+ C1:P2 . C2-3 P1 . . . 0 A1:0,A2:1 A1:P1,A2:P2" 343 344 # A non-exclusive cpuset.cpus change will invalidate partition and its siblings 345 " C0-1:P1 . . C2-3 C0-2 . . . 0 A1:0-2,B1:2-3 A1:P-1,B1:P0" 346 " C0-1:P1 . . P1:C2-3 C0-2 . . . 0 A1:0-2,B1:2-3 A1:P-1,B1:P-1" 347 " C0-1 . . P1:C2-3 C0-2 . . . 0 A1:0-2,B1:2-3 A1:P0,B1:P-1" 348 349 # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS 350 # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ -------- 351 # Failure cases: 352 353 # A task cannot be added to a partition with no cpu 354 "C2-3:P1:S+ C3:P1 . . O2=0:T . . . 1 A1:,A2:3 A1:P1,A2:P1" 355 356 # Changes to cpuset.cpus.exclusive that violate exclusivity rule is rejected 357 " C0-3 . . C4-5 X0-3 . . X3-5 1 A1:0-3,B1:4-5" 358) 359 360# 361# Write to the cpu online file 362# $1 - <c>=<v> where <c> = cpu number, <v> value to be written 363# 364write_cpu_online() 365{ 366 CPU=${1%=*} 367 VAL=${1#*=} 368 CPUFILE=//sys/devices/system/cpu/cpu${CPU}/online 369 if [[ $VAL -eq 0 ]] 370 then 371 OFFLINE_CPUS="$OFFLINE_CPUS $CPU" 372 else 373 [[ -n "$OFFLINE_CPUS" ]] && { 374 OFFLINE_CPUS=$(echo $CPU $CPU $OFFLINE_CPUS | fmt -1 |\ 375 sort | uniq -u) 376 } 377 fi 378 echo $VAL > $CPUFILE 379 pause 0.05 380} 381 382# 383# Set controller state 384# $1 - cgroup directory 385# $2 - state 386# $3 - showerr 387# 388# The presence of ":" in state means transition from one to the next. 389# 390set_ctrl_state() 391{ 392 TMPMSG=/tmp/.msg_$$ 393 CGRP=$1 394 STATE=$2 395 SHOWERR=${3} 396 CTRL=${CTRL:=$CONTROLLER} 397 HASERR=0 398 REDIRECT="2> $TMPMSG" 399 [[ -z "$STATE" || "$STATE" = '.' ]] && return 0 400 [[ $VERBOSE -gt 0 ]] && SHOWERR=1 401 402 rm -f $TMPMSG 403 for CMD in $(echo $STATE | sed -e "s/:/ /g") 404 do 405 TFILE=$CGRP/cgroup.procs 406 SFILE=$CGRP/cgroup.subtree_control 407 PFILE=$CGRP/cpuset.cpus.partition 408 CFILE=$CGRP/cpuset.cpus 409 XFILE=$CGRP/cpuset.cpus.exclusive 410 S=$(expr substr $CMD 1 1) 411 if [[ $S = S ]] 412 then 413 PREFIX=${CMD#?} 414 COMM="echo ${PREFIX}${CTRL} > $SFILE" 415 eval $COMM $REDIRECT 416 elif [[ $S = X ]] 417 then 418 CPUS=${CMD#?} 419 COMM="echo $CPUS > $XFILE" 420 eval $COMM $REDIRECT 421 elif [[ $S = C ]] 422 then 423 CPUS=${CMD#?} 424 COMM="echo $CPUS > $CFILE" 425 eval $COMM $REDIRECT 426 elif [[ $S = P ]] 427 then 428 VAL=${CMD#?} 429 case $VAL in 430 0) VAL=member 431 ;; 432 1) VAL=root 433 ;; 434 2) VAL=isolated 435 ;; 436 *) 437 echo "Invalid partition state - $VAL" 438 exit 1 439 ;; 440 esac 441 COMM="echo $VAL > $PFILE" 442 eval $COMM $REDIRECT 443 elif [[ $S = O ]] 444 then 445 VAL=${CMD#?} 446 write_cpu_online $VAL 447 elif [[ $S = T ]] 448 then 449 COMM="echo 0 > $TFILE" 450 eval $COMM $REDIRECT 451 fi 452 RET=$? 453 [[ $RET -ne 0 ]] && { 454 [[ -n "$SHOWERR" ]] && { 455 echo "$COMM" 456 cat $TMPMSG 457 } 458 HASERR=1 459 } 460 pause 0.01 461 rm -f $TMPMSG 462 done 463 return $HASERR 464} 465 466set_ctrl_state_noerr() 467{ 468 CGRP=$1 469 STATE=$2 470 [[ -d $CGRP ]] || mkdir $CGRP 471 set_ctrl_state $CGRP $STATE 1 472 [[ $? -ne 0 ]] && { 473 echo "ERROR: Failed to set $2 to cgroup $1!" 474 exit 1 475 } 476} 477 478online_cpus() 479{ 480 [[ -n "OFFLINE_CPUS" ]] && { 481 for C in $OFFLINE_CPUS 482 do 483 write_cpu_online ${C}=1 484 done 485 } 486} 487 488# 489# Return 1 if the list of effective cpus isn't the same as the initial list. 490# 491reset_cgroup_states() 492{ 493 echo 0 > $CGROUP2/cgroup.procs 494 online_cpus 495 rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1 496 pause 0.02 497 set_ctrl_state . R- 498 pause 0.01 499} 500 501dump_states() 502{ 503 for DIR in . A1 A1/A2 A1/A2/A3 B1 504 do 505 CPUS=$DIR/cpuset.cpus 506 ECPUS=$DIR/cpuset.cpus.effective 507 XCPUS=$DIR/cpuset.cpus.exclusive 508 XECPUS=$DIR/cpuset.cpus.exclusive.effective 509 PRS=$DIR/cpuset.cpus.partition 510 PCPUS=$DIR/.__DEBUG__.cpuset.cpus.subpartitions 511 ISCPUS=$DIR/cpuset.cpus.isolated 512 [[ -e $CPUS ]] && echo "$CPUS: $(cat $CPUS)" 513 [[ -e $XCPUS ]] && echo "$XCPUS: $(cat $XCPUS)" 514 [[ -e $ECPUS ]] && echo "$ECPUS: $(cat $ECPUS)" 515 [[ -e $XECPUS ]] && echo "$XECPUS: $(cat $XECPUS)" 516 [[ -e $PRS ]] && echo "$PRS: $(cat $PRS)" 517 [[ -e $PCPUS ]] && echo "$PCPUS: $(cat $PCPUS)" 518 [[ -e $ISCPUS ]] && echo "$ISCPUS: $(cat $ISCPUS)" 519 done 520} 521 522# 523# Check effective cpus 524# $1 - check string, format: <cgroup>:<cpu-list>[,<cgroup>:<cpu-list>]* 525# 526check_effective_cpus() 527{ 528 CHK_STR=$1 529 for CHK in $(echo $CHK_STR | sed -e "s/,/ /g") 530 do 531 set -- $(echo $CHK | sed -e "s/:/ /g") 532 CGRP=$1 533 CPUS=$2 534 if [[ $CGRP = X* ]] 535 then 536 CGRP=${CGRP#X} 537 FILE=cpuset.cpus.exclusive.effective 538 else 539 FILE=cpuset.cpus.effective 540 fi 541 [[ $CGRP = A2 ]] && CGRP=A1/A2 542 [[ $CGRP = A3 ]] && CGRP=A1/A2/A3 543 [[ -e $CGRP/$FILE ]] || return 1 544 [[ $CPUS = $(cat $CGRP/$FILE) ]] || return 1 545 done 546} 547 548# 549# Check cgroup states 550# $1 - check string, format: <cgroup>:<state>[,<cgroup>:<state>]* 551# 552check_cgroup_states() 553{ 554 CHK_STR=$1 555 for CHK in $(echo $CHK_STR | sed -e "s/,/ /g") 556 do 557 set -- $(echo $CHK | sed -e "s/:/ /g") 558 CGRP=$1 559 STATE=$2 560 FILE= 561 EVAL=$(expr substr $STATE 2 2) 562 [[ $CGRP = A2 ]] && CGRP=A1/A2 563 [[ $CGRP = A3 ]] && CGRP=A1/A2/A3 564 565 case $STATE in 566 P*) FILE=$CGRP/cpuset.cpus.partition 567 ;; 568 *) echo "Unknown state: $STATE!" 569 exit 1 570 ;; 571 esac 572 VAL=$(cat $FILE) 573 574 case "$VAL" in 575 member) VAL=0 576 ;; 577 root) VAL=1 578 ;; 579 isolated) 580 VAL=2 581 ;; 582 "root invalid"*) 583 VAL=-1 584 ;; 585 "isolated invalid"*) 586 VAL=-2 587 ;; 588 esac 589 [[ $EVAL != $VAL ]] && return 1 590 done 591 return 0 592} 593 594# 595# Get isolated (including offline) CPUs by looking at 596# /sys/kernel/debug/sched/domains and cpuset.cpus.isolated control file, 597# if available, and compare that with the expected value. 598# 599# Note that isolated CPUs from the sched/domains context include offline 600# CPUs as well as CPUs in non-isolated 1-CPU partition. Those CPUs may 601# not be included in the cpuset.cpus.isolated control file which contains 602# only CPUs in isolated partitions. 603# 604# $1 - expected isolated cpu list(s) <isolcpus1>{,<isolcpus2>} 605# <isolcpus1> - expected sched/domains value 606# <isolcpus2> - cpuset.cpus.isolated value = <isolcpus1> if not defined 607# 608check_isolcpus() 609{ 610 EXPECT_VAL=$1 611 ISOLCPUS= 612 LASTISOLCPU= 613 SCHED_DOMAINS=/sys/kernel/debug/sched/domains 614 ISCPUS=${CGROUP2}/cpuset.cpus.isolated 615 if [[ $EXPECT_VAL = . ]] 616 then 617 EXPECT_VAL= 618 EXPECT_VAL2= 619 elif [[ $(expr $EXPECT_VAL : ".*,.*") > 0 ]] 620 then 621 set -- $(echo $EXPECT_VAL | sed -e "s/,/ /g") 622 EXPECT_VAL=$1 623 EXPECT_VAL2=$2 624 else 625 EXPECT_VAL2=$EXPECT_VAL 626 fi 627 628 # 629 # Check the debug isolated cpumask, if present 630 # 631 [[ -f $ISCPUS ]] && { 632 ISOLCPUS=$(cat $ISCPUS) 633 [[ "$EXPECT_VAL2" != "$ISOLCPUS" ]] && { 634 # Take a 50ms pause and try again 635 pause 0.05 636 ISOLCPUS=$(cat $ISCPUS) 637 } 638 [[ "$EXPECT_VAL2" != "$ISOLCPUS" ]] && return 1 639 ISOLCPUS= 640 } 641 642 # 643 # Use the sched domain in debugfs to check isolated CPUs, if available 644 # 645 [[ -d $SCHED_DOMAINS ]] || return 0 646 647 for ((CPU=0; CPU < $NR_CPUS; CPU++)) 648 do 649 [[ -n "$(ls ${SCHED_DOMAINS}/cpu$CPU)" ]] && continue 650 651 if [[ -z "$LASTISOLCPU" ]] 652 then 653 ISOLCPUS=$CPU 654 LASTISOLCPU=$CPU 655 elif [[ "$LASTISOLCPU" -eq $((CPU - 1)) ]] 656 then 657 echo $ISOLCPUS | grep -q "\<$LASTISOLCPU\$" 658 if [[ $? -eq 0 ]] 659 then 660 ISOLCPUS=${ISOLCPUS}- 661 fi 662 LASTISOLCPU=$CPU 663 else 664 if [[ $ISOLCPUS = *- ]] 665 then 666 ISOLCPUS=${ISOLCPUS}$LASTISOLCPU 667 fi 668 ISOLCPUS=${ISOLCPUS},$CPU 669 LASTISOLCPU=$CPU 670 fi 671 done 672 [[ "$ISOLCPUS" = *- ]] && ISOLCPUS=${ISOLCPUS}$LASTISOLCPU 673 [[ "$EXPECT_VAL" = "$ISOLCPUS" ]] 674} 675 676test_fail() 677{ 678 TESTNUM=$1 679 TESTTYPE=$2 680 ADDINFO=$3 681 echo "Test $TEST[$TESTNUM] failed $TESTTYPE check!" 682 [[ -n "$ADDINFO" ]] && echo "*** $ADDINFO ***" 683 eval echo \${$TEST[$I]} 684 echo 685 dump_states 686 exit 1 687} 688 689# 690# Check to see if there are unexpected isolated CPUs left 691# 692null_isolcpus_check() 693{ 694 [[ $VERBOSE -gt 0 ]] || return 0 695 # Retry a few times before printing error 696 RETRY=0 697 while [[ $RETRY -lt 5 ]] 698 do 699 pause 0.01 700 check_isolcpus "." 701 [[ $? -eq 0 ]] && return 0 702 ((RETRY++)) 703 done 704 echo "Unexpected isolated CPUs: $ISOLCPUS" 705 dump_states 706 exit 1 707} 708 709# 710# Run cpuset state transition test 711# $1 - test matrix name 712# 713# This test is somewhat fragile as delays (sleep x) are added in various 714# places to make sure state changes are fully propagated before the next 715# action. These delays may need to be adjusted if running in a slower machine. 716# 717run_state_test() 718{ 719 TEST=$1 720 CONTROLLER=cpuset 721 I=0 722 eval CNT="\${#$TEST[@]}" 723 724 reset_cgroup_states 725 console_msg "Running state transition test ..." 726 727 while [[ $I -lt $CNT ]] 728 do 729 echo "Running test $I ..." > /dev/console 730 [[ $VERBOSE -gt 1 ]] && { 731 echo "" 732 eval echo \${$TEST[$I]} 733 } 734 eval set -- "\${$TEST[$I]}" 735 OLD_A1=$1 736 OLD_A2=$2 737 OLD_A3=$3 738 OLD_B1=$4 739 NEW_A1=$5 740 NEW_A2=$6 741 NEW_A3=$7 742 NEW_B1=$8 743 RESULT=$9 744 ECPUS=${10} 745 STATES=${11} 746 ICPUS=${12} 747 748 set_ctrl_state_noerr B1 $OLD_B1 749 set_ctrl_state_noerr A1 $OLD_A1 750 set_ctrl_state_noerr A1/A2 $OLD_A2 751 set_ctrl_state_noerr A1/A2/A3 $OLD_A3 752 RETVAL=0 753 set_ctrl_state A1 $NEW_A1; ((RETVAL += $?)) 754 set_ctrl_state A1/A2 $NEW_A2; ((RETVAL += $?)) 755 set_ctrl_state A1/A2/A3 $NEW_A3; ((RETVAL += $?)) 756 set_ctrl_state B1 $NEW_B1; ((RETVAL += $?)) 757 758 [[ $RETVAL -ne $RESULT ]] && test_fail $I result 759 760 [[ -n "$ECPUS" && "$ECPUS" != . ]] && { 761 check_effective_cpus $ECPUS 762 [[ $? -ne 0 ]] && test_fail $I "effective CPU" 763 } 764 765 [[ -n "$STATES" && "$STATES" != . ]] && { 766 check_cgroup_states $STATES 767 [[ $? -ne 0 ]] && test_fail $I states 768 } 769 770 # Compare the expected isolated CPUs with the actual ones, 771 # if available 772 [[ -n "$ICPUS" ]] && { 773 check_isolcpus $ICPUS 774 [[ $? -ne 0 ]] && test_fail $I "isolated CPU" \ 775 "Expect $ICPUS, get $ISOLCPUS instead" 776 } 777 reset_cgroup_states 778 # 779 # Check to see if effective cpu list changes 780 # 781 NEWLIST=$(cat cpuset.cpus.effective) 782 RETRY=0 783 while [[ $NEWLIST != $CPULIST && $RETRY -lt 8 ]] 784 do 785 # Wait a bit longer & recheck a few times 786 pause 0.01 787 ((RETRY++)) 788 NEWLIST=$(cat cpuset.cpus.effective) 789 done 790 [[ $NEWLIST != $CPULIST ]] && { 791 echo "Effective cpus changed to $NEWLIST after test $I!" 792 exit 1 793 } 794 null_isolcpus_check 795 [[ $VERBOSE -gt 0 ]] && echo "Test $I done." 796 ((I++)) 797 done 798 echo "All $I tests of $TEST PASSED." 799} 800 801# 802# Testing the new "isolated" partition root type 803# 804test_isolated() 805{ 806 cd $CGROUP2/test 807 echo 2-3 > cpuset.cpus 808 TYPE=$(cat cpuset.cpus.partition) 809 [[ $TYPE = member ]] || echo member > cpuset.cpus.partition 810 811 console_msg "Change from member to root" 812 test_partition root 813 814 console_msg "Change from root to isolated" 815 test_partition isolated 816 817 console_msg "Change from isolated to member" 818 test_partition member 819 820 console_msg "Change from member to isolated" 821 test_partition isolated 822 823 console_msg "Change from isolated to root" 824 test_partition root 825 826 console_msg "Change from root to member" 827 test_partition member 828 829 # 830 # Testing partition root with no cpu 831 # 832 console_msg "Distribute all cpus to child partition" 833 echo +cpuset > cgroup.subtree_control 834 test_partition root 835 836 mkdir A1 837 cd A1 838 echo 2-3 > cpuset.cpus 839 test_partition root 840 test_effective_cpus 2-3 841 cd .. 842 test_effective_cpus "" 843 844 console_msg "Moving task to partition test" 845 test_add_proc "No space left" 846 cd A1 847 test_add_proc "" 848 cd .. 849 850 console_msg "Shrink and expand child partition" 851 cd A1 852 echo 2 > cpuset.cpus 853 cd .. 854 test_effective_cpus 3 855 cd A1 856 echo 2-3 > cpuset.cpus 857 cd .. 858 test_effective_cpus "" 859 860 # Cleaning up 861 console_msg "Cleaning up" 862 echo $$ > $CGROUP2/cgroup.procs 863 [[ -d A1 ]] && rmdir A1 864 null_isolcpus_check 865} 866 867# 868# Wait for inotify event for the given file and read it 869# $1: cgroup file to wait for 870# $2: file to store the read result 871# 872wait_inotify() 873{ 874 CGROUP_FILE=$1 875 OUTPUT_FILE=$2 876 877 $WAIT_INOTIFY $CGROUP_FILE 878 cat $CGROUP_FILE > $OUTPUT_FILE 879} 880 881# 882# Test if inotify events are properly generated when going into and out of 883# invalid partition state. 884# 885test_inotify() 886{ 887 ERR=0 888 PRS=/tmp/.prs_$$ 889 cd $CGROUP2/test 890 [[ -f $WAIT_INOTIFY ]] || { 891 echo "wait_inotify not found, inotify test SKIPPED." 892 return 893 } 894 895 pause 0.01 896 echo 1 > cpuset.cpus 897 echo 0 > cgroup.procs 898 echo root > cpuset.cpus.partition 899 pause 0.01 900 rm -f $PRS 901 wait_inotify $PWD/cpuset.cpus.partition $PRS & 902 pause 0.01 903 set_ctrl_state . "O1=0" 904 pause 0.01 905 check_cgroup_states ".:P-1" 906 if [[ $? -ne 0 ]] 907 then 908 echo "FAILED: Inotify test - partition not invalid" 909 ERR=1 910 elif [[ ! -f $PRS ]] 911 then 912 echo "FAILED: Inotify test - event not generated" 913 ERR=1 914 kill %1 915 elif [[ $(cat $PRS) != "root invalid"* ]] 916 then 917 echo "FAILED: Inotify test - incorrect state" 918 cat $PRS 919 ERR=1 920 fi 921 online_cpus 922 echo member > cpuset.cpus.partition 923 echo 0 > ../cgroup.procs 924 if [[ $ERR -ne 0 ]] 925 then 926 exit 1 927 else 928 echo "Inotify test PASSED" 929 fi 930} 931 932trap cleanup 0 2 3 6 933run_state_test TEST_MATRIX 934test_isolated 935test_inotify 936echo "All tests PASSED." 937