xref: /linux/tools/testing/selftests/cgroup/test_cpuset_prs.sh (revision fbc988f236a60cf50a89da78fc6b7213daa9999c)
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 '{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# Check to see if /dev/console exists and is writable
32if [[ -c /dev/console && -w /dev/console ]]
33then
34	CONSOLE=/dev/console
35else
36	CONSOLE=/dev/null
37fi
38
39# Set verbose flag and delay factor
40PROG=$1
41VERBOSE=0
42DELAY_FACTOR=1
43SCHED_DEBUG=
44while [[ "$1" = -* ]]
45do
46	case "$1" in
47		-v) ((VERBOSE++))
48		    # Enable sched/verbose can slow thing down
49		    [[ $DELAY_FACTOR -eq 1 ]] &&
50			DELAY_FACTOR=2
51		    ;;
52		-d) DELAY_FACTOR=$2
53		    shift
54		    ;;
55		*)  echo "Usage: $PROG [-v] [-d <delay-factor>"
56		    exit
57		    ;;
58	esac
59	shift
60done
61
62# Set sched verbose flag if available when "-v" option is specified
63if [[ $VERBOSE -gt 0 && -d /sys/kernel/debug/sched ]]
64then
65	# Used to restore the original setting during cleanup
66	SCHED_DEBUG=$(cat /sys/kernel/debug/sched/verbose)
67	echo Y > /sys/kernel/debug/sched/verbose
68fi
69
70cd $CGROUP2
71echo +cpuset > cgroup.subtree_control
72
73#
74# If cpuset has been set up and used in child cgroups, we may not be able to
75# create partition under root cgroup because of the CPU exclusivity rule.
76# So we are going to skip the test if this is the case.
77#
78[[ -d test ]] || mkdir test
79echo 0-6 > test/cpuset.cpus
80echo root > test/cpuset.cpus.partition
81cat test/cpuset.cpus.partition | grep -q invalid
82RESULT=$?
83echo member > test/cpuset.cpus.partition
84echo "" > test/cpuset.cpus
85[[ $RESULT -eq 0 ]] && skip_test "Child cgroups are using cpuset!"
86
87#
88# If isolated CPUs have been reserved at boot time (as shown in
89# cpuset.cpus.isolated), these isolated CPUs should be outside of CPUs 0-8
90# that will be used by this script for testing purpose. If not, some of
91# the tests may fail incorrectly. Wait a bit and retry again just in case
92# these isolated CPUs are leftover from previous run and have just been
93# cleaned up earlier in this script.
94#
95# These pre-isolated CPUs should stay in an isolated state throughout the
96# testing process for now.
97#
98BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
99[[ -n "$BOOT_ISOLCPUS" ]] && {
100	sleep 0.5
101	BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
102}
103if [[ -n "$BOOT_ISOLCPUS" ]]
104then
105	[[ $(echo $BOOT_ISOLCPUS | sed -e "s/[,-].*//") -le 8 ]] &&
106		skip_test "Pre-isolated CPUs ($BOOT_ISOLCPUS) overlap CPUs to be tested"
107	echo "Pre-isolated CPUs: $BOOT_ISOLCPUS"
108fi
109
110cleanup()
111{
112	online_cpus
113	cd $CGROUP2
114	rmdir A1/A2/A3 A1/A2 A1 B1 test/A1 test/B1 test > /dev/null 2>&1
115	rmdir rtest/p1/c11 rtest/p1/c12 rtest/p2/c21 \
116	      rtest/p2/c22 rtest/p1 rtest/p2 rtest > /dev/null 2>&1
117	[[ -n "$SCHED_DEBUG" ]] &&
118		echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose
119}
120
121# Pause in ms
122pause()
123{
124	DELAY=$1
125	LOOP=0
126	while [[ $LOOP -lt $DELAY_FACTOR ]]
127	do
128		sleep $DELAY
129		((LOOP++))
130	done
131	return 0
132}
133
134console_msg()
135{
136	MSG=$1
137	echo "$MSG"
138	echo "" > $CONSOLE
139	echo "$MSG" > $CONSOLE
140	pause 0.01
141}
142
143test_partition()
144{
145	EXPECTED_VAL=$1
146	echo $EXPECTED_VAL > cpuset.cpus.partition
147	[[ $? -eq 0 ]] || exit 1
148	ACTUAL_VAL=$(cat cpuset.cpus.partition)
149	[[ $ACTUAL_VAL != $EXPECTED_VAL ]] && {
150		echo "cpuset.cpus.partition: expect $EXPECTED_VAL, found $ACTUAL_VAL"
151		echo "Test FAILED"
152		exit 1
153	}
154}
155
156test_effective_cpus()
157{
158	EXPECTED_VAL=$1
159	ACTUAL_VAL=$(cat cpuset.cpus.effective)
160	[[ "$ACTUAL_VAL" != "$EXPECTED_VAL" ]] && {
161		echo "cpuset.cpus.effective: expect '$EXPECTED_VAL', found '$ACTUAL_VAL'"
162		echo "Test FAILED"
163		exit 1
164	}
165}
166
167# Adding current process to cgroup.procs as a test
168test_add_proc()
169{
170	OUTSTR="$1"
171	ERRMSG=$((echo $$ > cgroup.procs) |& cat)
172	echo $ERRMSG | grep -q "$OUTSTR"
173	[[ $? -ne 0 ]] && {
174		echo "cgroup.procs: expect '$OUTSTR', got '$ERRMSG'"
175		echo "Test FAILED"
176		exit 1
177	}
178	echo $$ > $CGROUP2/cgroup.procs	# Move out the task
179}
180
181#
182# Cpuset controller state transition test matrix.
183#
184# Cgroup test hierarchy
185#
186#	      root
187#	        |
188#	 +------+------+
189#	 |             |
190#	 A1            B1
191#	 |
192#	 A2
193#	 |
194#	 A3
195#
196#  P<v> = set cpus.partition (0:member, 1:root, 2:isolated)
197#  C<l> = add cpu-list to cpuset.cpus
198#  X<l> = add cpu-list to cpuset.cpus.exclusive
199#  T    = put a task into cgroup
200#  CX<l> = add cpu-list to both cpuset.cpus and cpuset.cpus.exclusive
201#  O<c>=<v> = Write <v> to CPU online file of <c>
202#
203# ECPUs    - effective CPUs of cpusets
204# Pstate   - partition root state
205# ISOLCPUS - isolated CPUs (<icpus>[,<icpus2>])
206#
207# Note that if there are 2 fields in ISOLCPUS, the first one is for
208# sched-debug matching which includes offline CPUs and single-CPU partitions
209# while the second one is for matching cpuset.cpus.isolated.
210#
211SETUP_A123_PARTITIONS="C1-3:P1 C2-3:P1 C3:P1"
212TEST_MATRIX=(
213	#  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
214	#  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
215	"   C0-1     .      .    C2-3     .    C4-5     .      .     0 A2:0-1"
216	"   C0-1     .      .    C2-3    P1      .      .      .     0 "
217	"   C0-1     .      .    C2-3    P1   C0-1:P1   .      .     0 "
218	"   C0-1     .      .    C2-3    P1    C1:P1    .      .     0 "
219	"   C0-1     .      .    C2-3     .      .      .     P1     0 "
220	"  C0-1:P1   .      .    C2-3     .     C1      .      .     0 "
221	"  C0-1:P1   .      .    C2-3     .    C1:P1    .      .     0 "
222	"  C0-1:P1   .      .    C2-3     .    C1:P1    .     P1     0 "
223	"  C0-1:P1   .      .    C2-3   C4-5     .      .      .     0 A1:4-5"
224	"   C0-1     .      .   C2-3:P1   .      .      .     C2     0 "
225	"   C0-1     .      .   C2-3:P1   .      .      .    C4-5    0 B1:4-5"
226	"  C0-3:P1 C2-3:P1  .      .      .      .      .      .     0 A1:0-1|A2:2-3|XA2:2-3"
227	"  C0-3:P1 C2-3:P1  .      .     C1-3    .      .      .     0 A1:1|A2:2-3|XA2:2-3"
228	"  C2-3:P1  C3:P1   .      .     C3      .      .      .     0 A1:|A2:3|XA2:3 A1:P1|A2:P1"
229	"  C2-3:P1  C3:P1   .      .     C3      P0     .      .     0 A1:3|A2:3 A1:P1|A2:P0"
230	"  C2-3:P1  C2:P1   .      .     C2-4    .      .      .     0 A1:3-4|A2:2"
231	"  C2-3:P1  C3:P1   .      .     C3      .      .     C0-2   0 A1:|B1:0-2 A1:P1|A2:P1"
232	"$SETUP_A123_PARTITIONS    .     C2-3    .      .      .     0 A1:|A2:2|A3:3 A1:P1|A2:P1|A3:P1"
233
234	# CPU offlining cases:
235	"   C0-1     .      .    C2-3     .    C4-5     .     O2=0   0 A1:0-1|B1:3"
236	"  C0-3:P1 C2-3:P1  .      .     O2=0    .      .      .     0 A1:0-1|A2:3"
237	"  C0-3:P1 C2-3:P1  .      .     O2=0   O2=1    .      .     0 A1:0-1|A2:2-3"
238	"  C0-3:P1 C2-3:P1  .      .     O1=0    .      .      .     0 A1:0|A2:2-3"
239	"  C0-3:P1 C2-3:P1  .      .     O1=0   O1=1    .      .     0 A1:0-1|A2:2-3"
240	"  C2-3:P1  C3:P1   .      .     O3=0   O3=1    .      .     0 A1:2|A2:3 A1:P1|A2:P1"
241	"  C2-3:P1  C3:P2   .      .     O3=0   O3=1    .      .     0 A1:2|A2:3 A1:P1|A2:P2"
242	"  C2-3:P1  C3:P1   .      .     O2=0   O2=1    .      .     0 A1:2|A2:3 A1:P1|A2:P1"
243	"  C2-3:P1  C3:P2   .      .     O2=0   O2=1    .      .     0 A1:2|A2:3 A1:P1|A2:P2"
244	"  C2-3:P1  C3:P1   .      .     O2=0    .      .      .     0 A1:|A2:3 A1:P1|A2:P1"
245	"  C2-3:P1  C3:P1   .      .     O3=0    .      .      .     0 A1:2|A2: A1:P1|A2:P1"
246	"  C2-3:P1  C3:P1   .      .    T:O2=0   .      .      .     0 A1:3|A2:3 A1:P1|A2:P-1"
247	"  C2-3:P1  C3:P1   .      .      .    T:O3=0   .      .     0 A1:2|A2:2 A1:P1|A2:P-1"
248	"  C2-3:P1  C3:P2   .      .    T:O2=0   .      .      .     0 A1:3|A2:3 A1:P1|A2:P-2"
249	"  C1-3:P1  C3:P2   .      .      .    T:O3=0   .      .     0 A1:1-2|A2:1-2 A1:P1|A2:P-2 3|"
250	"  C1-3:P1  C3:P2   .      .      .    T:O3=0  O3=1    .     0 A1:1-2|A2:3 A1:P1|A2:P2  3"
251	"$SETUP_A123_PARTITIONS    .     O1=0    .      .      .     0 A1:|A2:2|A3:3 A1:P1|A2:P1|A3:P1"
252	"$SETUP_A123_PARTITIONS    .     O2=0    .      .      .     0 A1:1|A2:|A3:3 A1:P1|A2:P1|A3:P1"
253	"$SETUP_A123_PARTITIONS    .     O3=0    .      .      .     0 A1:1|A2:2|A3: A1:P1|A2:P1|A3:P1"
254	"$SETUP_A123_PARTITIONS    .    T:O1=0   .      .      .     0 A1:2-3|A2:2-3|A3:3 A1:P1|A2:P-1|A3:P-1"
255	"$SETUP_A123_PARTITIONS    .      .    T:O2=0   .      .     0 A1:1|A2:3|A3:3 A1:P1|A2:P1|A3:P-1"
256	"$SETUP_A123_PARTITIONS    .      .      .    T:O3=0   .     0 A1:1|A2:2|A3:2 A1:P1|A2:P1|A3:P-1"
257	"$SETUP_A123_PARTITIONS    .    T:O1=0  O1=1    .      .     0 A1:1|A2:2|A3:3 A1:P1|A2:P1|A3:P1"
258	"$SETUP_A123_PARTITIONS    .      .    T:O2=0  O2=1    .     0 A1:1|A2:2|A3:3 A1:P1|A2:P1|A3:P1"
259	"$SETUP_A123_PARTITIONS    .      .      .    T:O3=0  O3=1   0 A1:1|A2:2|A3:3 A1:P1|A2:P1|A3:P1"
260	"$SETUP_A123_PARTITIONS    .    T:O1=0  O2=0   O1=1    .     0 A1:1|A2:|A3:3 A1:P1|A2:P1|A3:P1"
261	"$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"
262
263	#  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
264	#  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
265	#
266	# Remote partition and cpuset.cpus.exclusive tests
267	#
268	"   C0-3    C1-3  C2-3     .    X2-3     .      .      .     0 A1:0-3|A2:1-3|A3:2-3|XA1:2-3"
269	"   C0-3    C1-3  C2-3     .    X2-3  X2-3:P2   .      .     0 A1:0-1|A2:2-3|A3:2-3 A1:P0|A2:P2 2-3"
270	"   C0-3    C1-3  C2-3     .    X2-3   X3:P2    .      .     0 A1:0-2|A2:3|A3:3 A1:P0|A2:P2 3"
271	"   C0-3    C1-3  C2-3     .    X2-3   X2-3  X2-3:P2   .     0 A1:0-1|A2:1|A3:2-3 A1:P0|A3:P2 2-3"
272	"   C0-3    C1-3  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"
273	"   C0-3    C1-3  C2-3   C2-3     .      .      .      P2    0 A1:0-1|A2:1|A3:1|B1:2-3 A1:P0|A3:P0|B1:P2"
274	"   C0-3    C1-3  C2-3   C4-5     .      .      .      P2    0 B1:4-5 B1:P2 4-5"
275	"   C0-3    C1-3  C2-3    C4    X2-3   X2-3  X2-3:P2   P2    0 A3:2-3|B1:4 A3:P2|B1:P2 2-4"
276	"   C0-3    C1-3  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"
277	"   C0-3    C1-3  C2-3    C4    X1-3  X1-3:P2   P2     .     0 A2:1|A3:2-3 A2:P2|A3:P2 1-3"
278	"   C0-3    C1-3  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"
279	"  C4:X0-3  X1-3  X2-3     .      .      P2     .      .     0 A1:4|A2:1-3|A3:1-3 A2:P2 1-3"
280	"  C4:X0-3  X1-3  X2-3     .      .      .      P2     .     0 A1:4|A2:4|A3:2-3 A3:P2 2-3"
281
282	# Nested remote/local partition tests
283	"   C0-3    C1-3  C2-3   C4-5   X2-3  X2-3:P1   P2     P1    0 A1:0-1|A2:|A3:2-3|B1:4-5 \
284								       A1:P0|A2:P1|A3:P2|B1:P1 2-3"
285	"   C0-3    C1-3  C2-3    C4    X2-3  X2-3:P1   P2     P1    0 A1:0-1|A2:|A3:2-3|B1:4 \
286								       A1:P0|A2:P1|A3:P2|B1:P1 2-4|2-3"
287	"   C0-3    C1-3  C2-3    C4    X2-3  X2-3:P1    .     P1    0 A1:0-1|A2:2-3|A3:2-3|B1:4 \
288								       A1:P0|A2:P1|A3:P0|B1:P1"
289	"   C0-3    C1-3   C3     C4    X2-3  X2-3:P1   P2     P1    0 A1:0-1|A2:2|A3:3|B1:4 \
290								       A1:P0|A2:P1|A3:P2|B1:P1 2-4|3"
291	"   C0-4    C1-4  C2-4     .    X2-4  X2-4:P2  X4:P1    .    0 A1:0-1|A2:2-3|A3:4 \
292								       A1:P0|A2:P2|A3:P1 2-4|2-3"
293	"   C0-4    C1-4  C2-4     .    X2-4  X2-4:P2 X3-4:P1   .    0 A1:0-1|A2:2|A3:3-4 \
294								       A1:P0|A2:P2|A3:P1 2"
295	" C0-4:X2-4 C1-4:X2-4:P2 C2-4:X4:P1 \
296				   .      .      X5      .      .    0 A1:0-4|A2:1-4|A3:2-4 \
297								       A1:P0|A2:P-2|A3:P-1 ."
298	" C0-4:X2-4 C1-4:X2-4:P2 C2-4:X4:P1 \
299				   .      .      .      X1      .    0 A1:0-1|A2:2-4|A3:2-4 \
300								       A1:P0|A2:P2|A3:P-1 2-4"
301	" CX1-3:P1 CX1-3  CX1-3    .      .      .      P1      .    0 A1:1-3|A2:1-3|A3:1-3 \
302								       A1:P1|A2:P0|A3:P-1"
303
304	# Remote partition offline tests
305	"   C0-3    C1-3  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"
306	"   C0-3    C1-3  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"
307	"   C0-3    C1-3   C3      .    X2-3   X2-3    P2:O3=0   .   0 A1:0-2|A2:1-2|A3: A1:P0|A3:P2 3"
308	"   C0-3    C1-3   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|"
309
310	# An invalidated remote partition cannot self-recover from hotplug
311	"   C0-3    C1-3   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 ."
312
313	# cpus.exclusive.effective clearing test
314	"   C0-3    C1-3   C2      .   X2-3:X    .      .      .     0 A1:0-3|A2:1-3|A3:2|XA1:"
315
316	# Invalid to valid remote partition transition test
317	"   C0-3    C1-3    .      .      .    X3:P2    .      .     0 A1:0-3|A2:1-3|XA2: A2:P-2 ."
318	"   C0-3 C1-3:X3:P2 .      .    X2-3    P2      .      .     0 A1:0-2|A2:3|XA2:3 A2:P2 3"
319
320	# Invalid to valid local partition direct transition tests
321	" C1-3:P2  X4:P2    .      .      .      .      .      .     0 A1:1-3|XA1:1-3|A2:1-3:XA2: A1:P2|A2:P-2 1-3"
322	" C1-3:P2  X4:P2    .      .      .    X3:P2    .      .     0 A1:1-2|XA1:1-3|A2:3:XA2:3 A1:P2|A2:P2 1-3"
323	" C0-3:P2    .      .    C4-6   C0-4     .      .      .     0 A1:0-4|B1:5-6 A1:P2|B1:P0"
324	" C0-3:P2    .      .    C4-6 C0-4:C0-3  .      .      .     0 A1:0-3|B1:4-6 A1:P2|B1:P0 0-3"
325
326	# Local partition invalidation tests
327	" C0-3:X1-3:P2 C1-3:X2-3:P2 C2-3:X3:P2 \
328				   .      .      .      .      .     0 A1:1|A2:2|A3:3 A1:P2|A2:P2|A3:P2 1-3"
329	" C0-3:X1-3:P2 C1-3:X2-3:P2 C2-3:X3:P2 \
330				   .      .     X4      .      .     0 A1:1-3|A2:1-3|A3:2-3|XA2:|XA3: A1:P2|A2:P-2|A3:P-2 1-3"
331	" C0-3:X1-3:P2 C1-3:X2-3:P2 C2-3:X3:P2 \
332				   .      .    C4:X     .      .     0 A1:1-3|A2:1-3|A3:2-3|XA2:|XA3: A1:P2|A2:P-2|A3:P-2 1-3"
333	# Local partition CPU change tests
334	" C0-5:P2  C4-5:P1  .      .      .    C3-5     .      .     0 A1:0-2|A2:3-5 A1:P2|A2:P1 0-2"
335	" C0-5:P2  C4-5:P1  .      .    C1-5     .      .      .     0 A1:1-3|A2:4-5 A1:P2|A2:P1 1-3"
336
337	# cpus_allowed/exclusive_cpus update tests
338	" C0-3:X2-3 C1-3:X2-3 C2-3:X2-3 \
339				   .    X:C4     .      P2     .     0 A1:4|A2:4|XA2:|XA3:|A3:4 \
340								       A1:P0|A3:P-2 ."
341	" C0-3:X2-3 C1-3:X2-3 C2-3:X2-3 \
342				   .     X1      .      P2     .     0 A1:0-3|A2:1-3|XA1:1|XA2:|XA3:|A3:2-3 \
343								       A1:P0|A3:P-2 ."
344	" C0-3:X2-3 C1-3:X2-3 C2-3:X2-3 \
345				   .      .     X3      P2     .     0 A1:0-2|A2:1-2|XA2:3|XA3:3|A3:3 \
346								       A1:P0|A3:P2 3"
347	" C0-3:X2-3 C1-3:X2-3 C2-3:X2-3:P2 \
348				   .      .     X3      .      .     0 A1:0-2|A2:1-2|XA2:3|XA3:3|A3:3|XA3:3 \
349								       A1:P0|A3:P2 3"
350	" C0-3:X2-3 C1-3:X2-3 C2-3:X2-3:P2 \
351				   .     X4      .      .      .     0 A1:0-3|A2:1-3|A3:2-3|XA1:4|XA2:|XA3 \
352								       A1:P0|A3:P-2"
353
354	#  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
355	#  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
356	#
357	# Incorrect change to cpuset.cpus[.exclusive] invalidates partition root
358	#
359	# Adding CPUs to partition root that are not in parent's
360	# cpuset.cpus is allowed, but those extra CPUs are ignored.
361	"  C2-3:P1   C3:P1  .      .      .     C2-4    .      .     0 A1:|A2:2-3 A1:P1|A2:P1"
362
363	# Taking away all CPUs from parent or itself if there are tasks
364	# will make the partition invalid.
365	"  C2-3:P1   C3:P1  .      .      T     C2-3    .      .     0 A1:2-3|A2:2-3 A1:P1|A2:P-1"
366	"   C3:P1     C3    .      .      T      P1     .      .     0 A1:3|A2:3 A1:P1|A2:P-1"
367	"$SETUP_A123_PARTITIONS    .    T:C2-3   .      .      .     0 A1:2-3|A2:2-3|A3:3 A1:P1|A2:P-1|A3:P-1"
368	"$SETUP_A123_PARTITIONS    . T:C2-3:C1-3 .      .      .     0 A1:1|A2:2|A3:3 A1:P1|A2:P1|A3:P1"
369
370	# Changing a partition root to member makes child partitions invalid
371	"  C2-3:P1   C3:P1  .      .      P0     .      .      .     0 A1:2-3|A2:3 A1:P0|A2:P-1"
372	"$SETUP_A123_PARTITIONS    .     C2-3    P0     .      .     0 A1:2-3|A2:2-3|A3:3 A1:P1|A2:P0|A3:P-1"
373
374	# cpuset.cpus can contains cpus not in parent's cpuset.cpus as long
375	# as they overlap.
376	"  C2-3:P1   .      .      .      .   C3-4:P1   .      .     0 A1:2|A2:3 A1:P1|A2:P1"
377
378	# Deletion of CPUs distributed to child cgroup is allowed.
379	"  C0-1:P1  C1      .    C2-3   C4-5     .      .      .     0 A1:4-5|A2:4-5"
380
381	# To become a valid partition root, cpuset.cpus must overlap parent's
382	# cpuset.cpus.
383	"  C0-1:P1   .      .    C2-3     .   C4-5:P1   .      .     0 A1:0-1|A2:0-1 A1:P1|A2:P-1"
384
385	# Enabling partition with child cpusets is allowed
386	"   C0-1    C1      .    C2-3    P1      .      .      .     0 A1:0-1|A2:1 A1:P1"
387
388	# A partition root with non-partition root parent is invalid| but it
389	# can be made valid if its parent becomes a partition root too.
390	"   C0-1    C1      .    C2-3     .      P2     .      .     0 A1:0-1|A2:1 A1:P0|A2:P-2"
391	"   C0-1   C1:P2    .    C2-3     P1     .      .      .     0 A1:0|A2:1 A1:P1|A2:P2 0-1|1"
392
393	# A non-exclusive cpuset.cpus change will not invalidate its siblings partition.
394	"  C0-1:P1   .      .    C2-3   C0-2     .      .      .     0 A1:0-2|B1:3 A1:P1|B1:P0"
395	"  C0-1:P1   .      .  P1:C2-3  C0-2     .      .      .     0 A1:0-1|XA1:0-1|B1:2-3 A1:P1|B1:P1"
396	"   C0-1     .      .  P1:C2-3  C0-2     .      .      .     0 A1:0-1|B1:2-3 A1:P0|B1:P1"
397
398	# cpuset.cpus can overlap with sibling cpuset.cpus.exclusive but not subsumed by it
399	"   C0-3     .      .    C4-5     X5     .      .      .     0 A1:0-3|B1:4-5"
400
401	# Child partition root that try to take all CPUs from parent partition
402	# with tasks will remain invalid.
403	"  C1-4:P1  P1      .      .       .     .      .      .     0 A1:1-4|A2:1-4 A1:P1|A2:P-1"
404	"  C1-4:P1  P1      .      .       .   C1-4     .      .     0 A1|A2:1-4 A1:P1|A2:P1"
405	"  C1-4:P1  P1      .      .       T   C1-4     .      .     0 A1:1-4|A2:1-4 A1:P1|A2:P-1"
406
407	# Clearing of cpuset.cpus with a preset cpuset.cpus.exclusive shouldn't
408	# affect cpuset.cpus.exclusive.effective.
409	"  C1-4:X3 C1:X3    .      .       .     C      .      .     0 A2:1-4|XA2:3"
410
411	# cpuset.cpus can contain CPUs that overlap a sibling cpuset with cpus.exclusive
412	# but creating a local partition out of it is not allowed. Similarly and change
413	# in cpuset.cpus of a local partition that overlaps sibling exclusive CPUs will
414	# invalidate it.
415	"  CX1-4  CX2-4:P2  .    C5-6      .     .      .      P1    0 A1:1|A2:2-4|B1:5-6|XB1:5-6 \
416								       A1:P0|A2:P2:B1:P1 2-4"
417	"  CX1-4  CX2-4:P2  .    C3-6      .     .      .      P1    0 A1:1|A2:2-4|B1:5-6 \
418								       A1:P0|A2:P2:B1:P-1 2-4"
419	"  CX1-4  CX2-4:P2  .    C5-6      .     .      .   P1:C3-6  0 A1:1|A2:2-4|B1:5-6 \
420								       A1:P0|A2:P2:B1:P-1 2-4"
421
422	# When multiple partitions with conflicting cpuset.cpus are created, the
423	# latter created ones will only get what are left of the available exclusive
424	# CPUs.
425	"  C1-3:P1   .      .      .       .     .      .   C3-5:P1  0 A1:1-3|B1:4-5:XB1:4-5 A1:P1|B1:P1"
426
427	# cpuset.cpus can be set to a subset of sibling's cpuset.cpus.exclusive
428	" C1-3:X1-3  .      .    C4-5      .     .      .     C1-2   0 A1:1-3|B1:1-2"
429
430	# cpuset.cpus can become empty with task in it as it inherits parent's effective CPUs
431	"   C1-3    C2      .      .       .    T:C     .      .     0 A1:1-3|A2:1-3"
432
433	#  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
434	#  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
435	# Failure cases:
436
437	# A task cannot be added to a partition with no cpu
438	"  C2-3:P1 C3:P1    .      .    O2=0:T   .      .      .     1 A1:|A2:3 A1:P1|A2:P1"
439
440	# Changes to cpuset.cpus.exclusive that violate exclusivity rule is rejected
441	"   C0-3     .      .    C4-5   X0-3     .      .     X3-5   1 A1:0-3|B1:4-5"
442
443	# cpuset.cpus.exclusive cannot be set to a superset of sibling's cpuset.cpus
444	"   C0-3     .      .    C4-5   X3-5     .      .      .     1 A1:0-3|B1:4-5"
445)
446
447#
448# Cpuset controller remote partition test matrix.
449#
450# Cgroup test hierarchy
451#
452#	      root
453#	        |
454#	      rtest (cpuset.cpus.exclusive=1-7)
455#	        |
456#	 +------+------+
457#	 |             |
458#	 p1            p2
459#     +--+--+       +--+--+
460#     |     |       |     |
461#    c11   c12     c21   c22
462#
463# REMOTE_TEST_MATRIX uses the same notational convention as TEST_MATRIX.
464# Only CPUs 1-7 should be used.
465#
466REMOTE_TEST_MATRIX=(
467	#  old-p1 old-p2 old-c11 old-c12 old-c21 old-c22
468	#  new-p1 new-p2 new-c11 new-c12 new-c21 new-c22 ECPUs Pstate ISOLCPUS
469	#  ------ ------ ------- ------- ------- ------- ----- ------ --------
470	"   X1-3   X4-6  X1-2      X3     X4-5     X6 \
471	      .      .     P2      P2      P2      P2    c11:1-2|c12:3|c21:4-5|c22:6 \
472							 c11:P2|c12:P2|c21:P2|c22:P2 1-6"
473	"  CX1-4     .  X1-2:P2    C3      .       .  \
474	      .      .     .      C3-4     .       .     p1:3-4|c11:1-2|c12:3-4 \
475							 p1:P0|c11:P2|c12:P0 1-2"
476	"  CX1-4     .  X1-2:P2    .       .       .  \
477	    X2-4     .     .       .       .       .     p1:1,3-4|c11:2 \
478							 p1:P0|c11:P2 2"
479	"  CX1-5     .  X1-2:P2  X3-5:P1   .       .  \
480	    X2-4     .     .       .       .       .     p1:1,5|c11:2|c12:3-4 \
481							 p1:P0|c11:P2|c12:P1 2"
482	"  CX1-4     .  X1-2:P2  X3-4:P1   .       .  \
483	      .      .     X2      .       .       .     p1:1|c11:2|c12:3-4 \
484							 p1:P0|c11:P2|c12:P1 2"
485	# p1 as member, will get its effective CPUs from its parent rtest
486	"  CX1-4     .  X1-2:P2  X3-4:P1   .       .  \
487	      .      .     X1     CX2-4    .       .     p1:5-7|c11:1|c12:2-4 \
488							 p1:P0|c11:P2|c12:P1 1"
489	"  CX1-4  X5-6:P1  .       .       .       .  \
490	      .      .  X1-2:P2  X4-5:P1   .     X1-7:P2 p1:3|c11:1-2|c12:4:c22:5-6 \
491							 p1:P0|p2:P1|c11:P2|c12:P1|c22:P2 \
492							 1-2,4-6|1-2,5-6"
493	# c12 whose cpuset.cpus CPUs are all granted to c11 will become invalid partition
494	"  C1-5:P1   .  C1-4:P1   C2-3     .       .  \
495	      .      .     .       P1      .       .     p1:5|c11:1-4|c12:5 \
496							 p1:P1|c11:P1|c12:P-1"
497	# Narrowing cpuset.cpus to previously sibling-excluded CPUs should
498	# not return CPUs that were never actually owned.
499	"  C1-4:P1   .   C1-2:P1  C1-3:P2  .       .  \
500	      .      .     .       C3      .       .     p1:4|c11:1-2|c12:3 \
501							 p1:P1|c11:P1|c12:P2 3"
502	# Expanding cpuset.cpus to include a previously sibling-excluded CPU
503	# after the sibling has become a member should correctly request it.
504	"  C1-4:P1   .   C1-2:P1  C1-3:P2  .       .  \
505	      .      .     P0      C2-3    .       .     p1:1,4|c11:1|c12:2-3 \
506							 p1:P1|c11:P0|c12:P2 2-3"
507	# Changing a sibling partition's cpuset.cpus to overlap with another
508	# sibling partition should invalidate itself and return only actually
509	# allocated CPUs (effective_xcpus) to the parent.
510	"  C1-4:P1   .   C1-2:P1  C2-4:P2  .       .  \
511	      .      .     .       C1-2    .       .     p1:3-4|c11:1-2|c12:3-4 \
512							 p1:P1|c11:P1|c12:P-2"
513	# Cpusets with empty cpuset.cpus should inherit parent's effective_cpus
514	"  C1-4:P1 C5-6   C1-2     .       C5      .  \
515	      .      P1    P1      .       .       .     p1:3-4|p2:5-6|c11:1-2|c12:3-4|c21:5|c22:5-6 \
516							 p1:P1|p2:P1|c11:P1"
517	"  C1-4:P1 C5-6   C1-2     .       C5      .  \
518	      .      P1    P1      .      O5=0     .     p1:3-4|p2:6|c11:1-2|c12:3-4|c21:6|c22:6 \
519							 p1:P1|p2:P1|c11:P1"
520)
521
522#
523# Write to the cpu online file
524#  $1 - <c>=<v> where <c> = cpu number, <v> value to be written
525#
526write_cpu_online()
527{
528	CPU=${1%=*}
529	VAL=${1#*=}
530	CPUFILE=//sys/devices/system/cpu/cpu${CPU}/online
531	echo $VAL > $CPUFILE || return 1
532	if [[ $VAL -eq 0 ]]
533	then
534		OFFLINE_CPUS="$OFFLINE_CPUS $CPU"
535	else
536		[[ -n "$OFFLINE_CPUS" ]] && {
537			OFFLINE_CPUS=$(echo $CPU $CPU $OFFLINE_CPUS | fmt -1 |\
538					sort | uniq -u)
539		}
540	fi
541	pause 0.05
542}
543
544#
545# Set controller state
546#  $1 - cgroup directory
547#  $2 - state
548#  $3 - showerr
549#
550# The presence of ":" in state means transition from one to the next.
551#
552set_ctrl_state()
553{
554	TMPMSG=/tmp/.msg_$$
555	CGRP=$1
556	STATE=$2
557	SHOWERR=${3}
558	HASERR=0
559	REDIRECT="2> $TMPMSG"
560	[[ -z "$STATE" || "$STATE" = '.' ]] && return 0
561	[[ $VERBOSE -gt 0 ]] && SHOWERR=1
562
563	rm -f $TMPMSG
564	for CMD in $(echo $STATE | sed -e "s/:/ /g")
565	do
566		TFILE=$CGRP/cgroup.procs
567		PFILE=$CGRP/cpuset.cpus.partition
568		CFILE=$CGRP/cpuset.cpus
569		XFILE=$CGRP/cpuset.cpus.exclusive
570
571		# Enable cpuset controller if not enabled yet
572		[[ -f $CFILE ]] || {
573			COMM="echo +cpuset > $CGRP/../cgroup.subtree_control"
574			eval $COMM $REDIRECT
575		}
576		case $CMD in
577		    X*)
578			CPUS=${CMD#?}
579			COMM="echo $CPUS > $XFILE"
580			eval $COMM $REDIRECT
581			;;
582		    CX*)
583			CPUS=${CMD#??}
584			COMM="echo $CPUS > $CFILE; echo $CPUS > $XFILE"
585			eval $COMM $REDIRECT
586			;;
587		    C*) CPUS=${CMD#?}
588			COMM="echo $CPUS > $CFILE"
589			eval $COMM $REDIRECT
590			;;
591		    P*) VAL=${CMD#?}
592			case $VAL in
593			0)  VAL=member
594			    ;;
595			1)  VAL=root
596			    ;;
597			2)  VAL=isolated
598			    ;;
599			*)
600			    echo "Invalid partition state - $VAL"
601			    exit 1
602			    ;;
603			esac
604			COMM="echo $VAL > $PFILE"
605			eval $COMM $REDIRECT
606			;;
607		    O*) VAL=${CMD#?}
608			COMM="write_cpu_online $VAL"
609			eval $COMM $REDIRECT
610			;;
611		    T*) COMM="echo 0 > $TFILE"
612			eval $COMM $REDIRECT
613			;;
614		    *)  echo "Unknown command: $CMD"
615		        exit 1
616			;;
617		esac
618		RET=$?
619		[[ $RET -ne 0 ]] && {
620			[[ -n "$SHOWERR" ]] && {
621				echo "$COMM"
622				cat $TMPMSG
623			}
624			HASERR=1
625		}
626		pause 0.01
627		rm -f $TMPMSG
628	done
629	return $HASERR
630}
631
632set_ctrl_state_noerr()
633{
634	CGRP=$1
635	STATE=$2
636	[[ -d $CGRP ]] || mkdir $CGRP
637	set_ctrl_state $CGRP $STATE 1
638	[[ $? -ne 0 ]] && {
639		echo "ERROR: Failed to set $2 to cgroup $1!"
640		exit 1
641	}
642}
643
644online_cpus()
645{
646	[[ -n "$OFFLINE_CPUS" ]] && {
647		for C in $OFFLINE_CPUS
648		do
649			write_cpu_online ${C}=1
650		done
651	}
652}
653
654#
655# Remove all the test cgroup directories
656#
657reset_cgroup_states()
658{
659	echo 0 > $CGROUP2/cgroup.procs
660	online_cpus
661	rmdir $RESET_LIST > /dev/null 2>&1
662}
663
664dump_states()
665{
666	for DIR in $CGROUP_LIST
667	do
668		CPUS=$DIR/cpuset.cpus
669		ECPUS=$DIR/cpuset.cpus.effective
670		XCPUS=$DIR/cpuset.cpus.exclusive
671		XECPUS=$DIR/cpuset.cpus.exclusive.effective
672		PRS=$DIR/cpuset.cpus.partition
673		PCPUS=$DIR/.__DEBUG__.cpuset.cpus.subpartitions
674		ISCPUS=$DIR/cpuset.cpus.isolated
675		[[ -e $CPUS   ]] && echo "$CPUS: $(cat $CPUS)"
676		[[ -e $XCPUS  ]] && echo "$XCPUS: $(cat $XCPUS)"
677		[[ -e $ECPUS  ]] && echo "$ECPUS: $(cat $ECPUS)"
678		[[ -e $XECPUS ]] && echo "$XECPUS: $(cat $XECPUS)"
679		[[ -e $PRS    ]] && echo "$PRS: $(cat $PRS)"
680		[[ -e $PCPUS  ]] && echo "$PCPUS: $(cat $PCPUS)"
681		[[ -e $ISCPUS ]] && echo "$ISCPUS: $(cat $ISCPUS)"
682	done
683}
684
685#
686# Set the actual cgroup directory into $CGRP_DIR
687# $1 - cgroup name
688#
689set_cgroup_dir()
690{
691	CGRP_DIR=$1
692	[[ $CGRP_DIR = A2  ]] && CGRP_DIR=A1/A2
693	[[ $CGRP_DIR = A3  ]] && CGRP_DIR=A1/A2/A3
694	[[ $CGRP_DIR = c11 ]] && CGRP_DIR=p1/c11
695	[[ $CGRP_DIR = c12 ]] && CGRP_DIR=p1/c12
696	[[ $CGRP_DIR = c21 ]] && CGRP_DIR=p2/c21
697	[[ $CGRP_DIR = c22 ]] && CGRP_DIR=p2/c22
698}
699
700#
701# Check effective cpus
702# $1 - check string, format: <cgroup>:<cpu-list>[|<cgroup>:<cpu-list>]*
703#
704check_effective_cpus()
705{
706	CHK_STR=$1
707	for CHK in $(echo $CHK_STR | sed -e "s/|/ /g")
708	do
709		set -- $(echo $CHK | sed -e "s/:/ /g")
710		CGRP=$1
711		EXPECTED_CPUS=$2
712		ACTUAL_CPUS=
713		if [[ $CGRP = X* ]]
714		then
715			CGRP=${CGRP#X}
716			FILE=cpuset.cpus.exclusive.effective
717		else
718			FILE=cpuset.cpus.effective
719		fi
720		set_cgroup_dir $CGRP
721		[[ -e $CGRP_DIR/$FILE ]] || return 1
722		ACTUAL_CPUS=$(cat $CGRP_DIR/$FILE)
723		[[ $EXPECTED_CPUS = $ACTUAL_CPUS ]] || return 1
724	done
725}
726
727#
728# Check cgroup states
729#  $1 - check string, format: <cgroup>:<state>[|<cgroup>:<state>]*
730#
731check_cgroup_states()
732{
733	CHK_STR=$1
734	for CHK in $(echo $CHK_STR | sed -e "s/|/ /g")
735	do
736		set -- $(echo $CHK | sed -e "s/:/ /g")
737		CGRP=$1
738		EXPECTED_STATE=$2
739		FILE=
740		EVAL=$(expr substr $EXPECTED_STATE 2 2)
741
742		set_cgroup_dir $CGRP
743		case $EXPECTED_STATE in
744			P*) FILE=$CGRP_DIR/cpuset.cpus.partition
745			    ;;
746			*)  echo "Unknown state: $EXPECTED_STATE!"
747			    exit 1
748			    ;;
749		esac
750		ACTUAL_STATE=$(cat $FILE)
751
752		case "$ACTUAL_STATE" in
753			member) VAL=0
754				;;
755			root)	VAL=1
756				;;
757			isolated)
758				VAL=2
759				;;
760			"root invalid"*)
761				VAL=-1
762				;;
763			"isolated invalid"*)
764				VAL=-2
765				;;
766		esac
767		[[ $EVAL != $VAL ]] && return 1
768
769		#
770		# For root partition, dump sched-domains info to console if
771		# verbose mode set for manual comparison with sched debug info.
772		#
773		[[ $VAL -eq 1 && $VERBOSE -gt 0 ]] && {
774			DOMS=$(cat $CGRP_DIR/cpuset.cpus.effective)
775			[[ -n "$DOMS" ]] &&
776				echo " [$CGRP_DIR] sched-domain: $DOMS" > $CONSOLE
777		}
778	done
779	return 0
780}
781
782#
783# Get isolated (including offline) CPUs by looking at
784# /sys/kernel/debug/sched/domains and cpuset.cpus.isolated control file,
785# if available, and compare that with the expected value.
786#
787# Note that isolated CPUs from the sched/domains context include offline
788# CPUs as well as CPUs in non-isolated 1-CPU partition. Those CPUs may
789# not be included in the cpuset.cpus.isolated control file which contains
790# only CPUs in isolated partitions as well as those that are isolated at
791# boot time.
792#
793# $1 - expected isolated cpu list(s) <isolcpus1>{|<isolcpus2>}
794# <isolcpus1> - expected sched/domains value
795# <isolcpus2> - cpuset.cpus.isolated value = <isolcpus1> if not defined
796#
797check_isolcpus()
798{
799	EXPECTED_ISOLCPUS=$1
800	ISCPUS=${CGROUP2}/cpuset.cpus.isolated
801	ISOLCPUS=$(cat $ISCPUS)
802	LASTISOLCPU=
803	SCHED_DOMAINS=/sys/kernel/debug/sched/domains
804	if [[ $EXPECTED_ISOLCPUS = . ]]
805	then
806		EXPECTED_ISOLCPUS=
807		EXPECTED_SDOMAIN=
808	elif [[ $(expr $EXPECTED_ISOLCPUS : ".*|.*") > 0 ]]
809	then
810		set -- $(echo $EXPECTED_ISOLCPUS | sed -e "s/|/ /g")
811		EXPECTED_ISOLCPUS=$2
812		EXPECTED_SDOMAIN=$1
813	else
814		EXPECTED_SDOMAIN=$EXPECTED_ISOLCPUS
815	fi
816
817	#
818	# Appending pre-isolated CPUs
819	# Even though CPU #8 isn't used for testing, it can't be pre-isolated
820	# to make appending those CPUs easier.
821	#
822	[[ -n "$BOOT_ISOLCPUS" ]] && {
823		EXPECTED_ISOLCPUS=${EXPECTED_ISOLCPUS:+${EXPECTED_ISOLCPUS},}${BOOT_ISOLCPUS}
824		EXPECTED_SDOMAIN=${EXPECTED_SDOMAIN:+${EXPECTED_SDOMAIN},}${BOOT_ISOLCPUS}
825	}
826
827	#
828	# Check cpuset.cpus.isolated cpumask
829	#
830	[[ "$EXPECTED_ISOLCPUS" != "$ISOLCPUS" ]] && {
831		# Take a 50ms pause and try again
832		pause 0.05
833		ISOLCPUS=$(cat $ISCPUS)
834	}
835	[[ "$EXPECTED_ISOLCPUS" != "$ISOLCPUS" ]] && return 1
836	ISOLCPUS=
837	EXPECTED_ISOLCPUS=$EXPECTED_SDOMAIN
838
839	#
840	# Use the sched domain in debugfs to check isolated CPUs, if available
841	#
842	[[ -d $SCHED_DOMAINS ]] || return 0
843
844	for ((CPU=0; CPU < $NR_CPUS; CPU++))
845	do
846		[[ -n "$(ls ${SCHED_DOMAINS}/cpu$CPU)" ]] && continue
847
848		if [[ -z "$LASTISOLCPU" ]]
849		then
850			ISOLCPUS=$CPU
851			LASTISOLCPU=$CPU
852		elif [[ "$LASTISOLCPU" -eq $((CPU - 1)) ]]
853		then
854			echo $ISOLCPUS | grep -q "\<$LASTISOLCPU\$"
855			if [[ $? -eq 0 ]]
856			then
857				ISOLCPUS=${ISOLCPUS}-
858			fi
859			LASTISOLCPU=$CPU
860		else
861			if [[ $ISOLCPUS = *- ]]
862			then
863				ISOLCPUS=${ISOLCPUS}$LASTISOLCPU
864			fi
865			ISOLCPUS=${ISOLCPUS},$CPU
866			LASTISOLCPU=$CPU
867		fi
868	done
869	[[ "$ISOLCPUS" = *- ]] && ISOLCPUS=${ISOLCPUS}$LASTISOLCPU
870
871	[[ "$EXPECTED_SDOMAIN" = "$ISOLCPUS" ]]
872}
873
874test_fail()
875{
876	TESTNUM=$1
877	TESTTYPE=$2
878	ADDINFO=$3
879	echo "Test $TEST[$TESTNUM] failed $TESTTYPE check!"
880	[[ -n "$ADDINFO" ]] && echo "*** $ADDINFO ***"
881	eval echo \${$TEST[$I]}
882	echo
883	dump_states
884	exit 1
885}
886
887#
888# Check to see if there are unexpected isolated CPUs left beyond the boot
889# time isolated ones.
890#
891null_isolcpus_check()
892{
893	[[ $VERBOSE -gt 0 ]] || return 0
894	# Retry a few times before printing error
895	RETRY=0
896	while [[ $RETRY -lt 8 ]]
897	do
898		pause 0.02
899		check_isolcpus "."
900		[[ $? -eq 0 ]] && return 0
901		((RETRY++))
902	done
903	echo "Unexpected isolated CPUs: $ISOLCPUS"
904	dump_states
905	exit 1
906}
907
908#
909# Check state transition test result
910#  $1 - Test number
911#  $2 - Expected effective CPU values
912#  $3 - Expected partition states
913#  $4 - Expected isolated CPUs
914#
915check_test_results()
916{
917	_NR=$1
918	_ECPUS="$2"
919	_PSTATES="$3"
920	_ISOLCPUS="$4"
921
922	[[ -n "$_ECPUS" && "$_ECPUS" != . ]] && {
923		check_effective_cpus $_ECPUS
924		[[ $? -ne 0 ]] && test_fail $_NR "effective CPU" \
925			 "Cgroup $CGRP: expected $EXPECTED_CPUS, got $ACTUAL_CPUS"
926	}
927
928	[[ -n "$_PSTATES" && "$_PSTATES" != . ]] && {
929		check_cgroup_states $_PSTATES
930		[[ $? -ne 0 ]] && test_fail $_NR states \
931			"Cgroup $CGRP: expected $EXPECTED_STATE, got $ACTUAL_STATE"
932	}
933
934	# Compare the expected isolated CPUs with the actual ones,
935	# if available
936	[[ -n "$_ISOLCPUS" ]] && {
937		check_isolcpus $_ISOLCPUS
938		[[ $? -ne 0 ]] && {
939			[[ -n "$BOOT_ISOLCPUS" ]] && _ISOLCPUS=${_ISOLCPUS},${BOOT_ISOLCPUS}
940			test_fail $_NR "isolated CPU" \
941				"Expect $_ISOLCPUS, get $ISOLCPUS instead"
942		}
943	}
944	reset_cgroup_states
945	#
946	# Check to see if effective cpu list changes
947	#
948	_NEWLIST=$(cat $CGROUP2/cpuset.cpus.effective)
949	RETRY=0
950	while [[ $_NEWLIST != $CPULIST && $RETRY -lt 8 ]]
951	do
952		# Wait a bit longer & recheck a few times
953		pause 0.02
954		((RETRY++))
955		_NEWLIST=$(cat $CGROUP2/cpuset.cpus.effective)
956	done
957	[[ $_NEWLIST != $CPULIST ]] && {
958		echo "Effective cpus changed to $_NEWLIST after test $_NR!"
959		exit 1
960	}
961	null_isolcpus_check
962	[[ $VERBOSE -gt 0 ]] && echo "Test $I done."
963}
964
965#
966# Run cpuset state transition test
967#  $1 - test matrix name
968#
969# This test is somewhat fragile as delays (sleep x) are added in various
970# places to make sure state changes are fully propagated before the next
971# action. These delays may need to be adjusted if running in a slower machine.
972#
973run_state_test()
974{
975	TEST=$1
976	CGROUP_LIST=". A1 A1/A2 A1/A2/A3 B1"
977	RESET_LIST="A1/A2/A3 A1/A2 A1 B1"
978	I=0
979	eval CNT="\${#$TEST[@]}"
980
981	reset_cgroup_states
982	console_msg "Running state transition test ..."
983
984	while [[ $I -lt $CNT ]]
985	do
986		echo "Running test $I ..." > $CONSOLE
987		[[ $VERBOSE -gt 1 ]] && {
988			echo ""
989			eval echo \${$TEST[$I]}
990		}
991		eval set -- "\${$TEST[$I]}"
992		OLD_A1=$1
993		OLD_A2=$2
994		OLD_A3=$3
995		OLD_B1=$4
996		NEW_A1=$5
997		NEW_A2=$6
998		NEW_A3=$7
999		NEW_B1=$8
1000		RESULT=$9
1001		ECPUS=${10}
1002		STATES=${11}
1003		ICPUS=${12}
1004
1005		set_ctrl_state_noerr A1       $OLD_A1
1006		set_ctrl_state_noerr A1/A2    $OLD_A2
1007		set_ctrl_state_noerr A1/A2/A3 $OLD_A3
1008		set_ctrl_state_noerr B1       $OLD_B1
1009
1010		RETVAL=0
1011		set_ctrl_state A1       $NEW_A1; ((RETVAL += $?))
1012		set_ctrl_state A1/A2    $NEW_A2; ((RETVAL += $?))
1013		set_ctrl_state A1/A2/A3 $NEW_A3; ((RETVAL += $?))
1014		set_ctrl_state B1       $NEW_B1; ((RETVAL += $?))
1015
1016		[[ $RETVAL -ne $RESULT ]] && test_fail $I result
1017
1018		check_test_results $I "$ECPUS" "$STATES" "$ICPUS"
1019		((I++))
1020	done
1021	echo "All $I tests of $TEST PASSED."
1022}
1023
1024#
1025# Run cpuset remote partition state transition test
1026#  $1 - test matrix name
1027#
1028run_remote_state_test()
1029{
1030	TEST=$1
1031	[[ -d rtest ]] || mkdir rtest
1032	cd rtest
1033	echo +cpuset > cgroup.subtree_control
1034	echo "1-7" > cpuset.cpus
1035	echo "1-7" > cpuset.cpus.exclusive
1036	CGROUP_LIST=".. . p1 p2 p1/c11 p1/c12 p2/c21 p2/c22"
1037	RESET_LIST="p1/c11 p1/c12 p2/c21 p2/c22 p1 p2"
1038	I=0
1039	eval CNT="\${#$TEST[@]}"
1040
1041	reset_cgroup_states
1042	console_msg "Running remote partition state transition test ..."
1043
1044	while [[ $I -lt $CNT ]]
1045	do
1046		echo "Running test $I ..." > $CONSOLE
1047		[[ $VERBOSE -gt 1 ]] && {
1048			echo ""
1049			eval echo \${$TEST[$I]}
1050		}
1051		eval set -- "\${$TEST[$I]}"
1052		OLD_p1=$1
1053		OLD_p2=$2
1054		OLD_c11=$3
1055		OLD_c12=$4
1056		OLD_c21=$5
1057		OLD_c22=$6
1058		NEW_p1=$7
1059		NEW_p2=$8
1060		NEW_c11=$9
1061		NEW_c12=${10}
1062		NEW_c21=${11}
1063		NEW_c22=${12}
1064		ECPUS=${13}
1065		STATES=${14}
1066		ICPUS=${15}
1067
1068		set_ctrl_state_noerr p1     $OLD_p1
1069		set_ctrl_state_noerr p2     $OLD_p2
1070		set_ctrl_state_noerr p1/c11 $OLD_c11
1071		set_ctrl_state_noerr p1/c12 $OLD_c12
1072		set_ctrl_state_noerr p2/c21 $OLD_c21
1073		set_ctrl_state_noerr p2/c22 $OLD_c22
1074
1075		RETVAL=0
1076		set_ctrl_state p1     $NEW_p1 ; ((RETVAL += $?))
1077		set_ctrl_state p2     $NEW_p2 ; ((RETVAL += $?))
1078		set_ctrl_state p1/c11 $NEW_c11; ((RETVAL += $?))
1079		set_ctrl_state p1/c12 $NEW_c12; ((RETVAL += $?))
1080		set_ctrl_state p2/c21 $NEW_c21; ((RETVAL += $?))
1081		set_ctrl_state p2/c22 $NEW_c22; ((RETVAL += $?))
1082
1083		[[ $RETVAL -ne 0 ]] && test_fail $I result
1084
1085		check_test_results $I "$ECPUS" "$STATES" "$ICPUS"
1086		((I++))
1087	done
1088	cd ..
1089	rmdir rtest
1090	echo "All $I tests of $TEST PASSED."
1091}
1092
1093#
1094# Testing the new "isolated" partition root type
1095#
1096test_isolated()
1097{
1098	cd $CGROUP2/test
1099	echo 2-3 > cpuset.cpus
1100	TYPE=$(cat cpuset.cpus.partition)
1101	[[ $TYPE = member ]] || echo member > cpuset.cpus.partition
1102
1103	console_msg "Change from member to root"
1104	test_partition root
1105
1106	console_msg "Change from root to isolated"
1107	test_partition isolated
1108
1109	console_msg "Change from isolated to member"
1110	test_partition member
1111
1112	console_msg "Change from member to isolated"
1113	test_partition isolated
1114
1115	console_msg "Change from isolated to root"
1116	test_partition root
1117
1118	console_msg "Change from root to member"
1119	test_partition member
1120
1121	#
1122	# Testing partition root with no cpu
1123	#
1124	console_msg "Distribute all cpus to child partition"
1125	echo +cpuset > cgroup.subtree_control
1126	test_partition root
1127
1128	mkdir A1
1129	cd A1
1130	echo 2-3 > cpuset.cpus
1131	test_partition root
1132	test_effective_cpus 2-3
1133	cd ..
1134	test_effective_cpus ""
1135
1136	console_msg "Moving task to partition test"
1137	test_add_proc "No space left"
1138	cd A1
1139	test_add_proc ""
1140	cd ..
1141
1142	console_msg "Shrink and expand child partition"
1143	cd A1
1144	echo 2 > cpuset.cpus
1145	cd ..
1146	test_effective_cpus 3
1147	cd A1
1148	echo 2-3 > cpuset.cpus
1149	cd ..
1150	test_effective_cpus ""
1151
1152	# Cleaning up
1153	console_msg "Cleaning up"
1154	echo $$ > $CGROUP2/cgroup.procs
1155	[[ -d A1 ]] && rmdir A1
1156	null_isolcpus_check
1157	pause 0.05
1158}
1159
1160#
1161# Select an online CPU isolated from scheduler domains at boot.
1162# $1: test name used in the skip message
1163#
1164get_boot_isolated_cpu()
1165{
1166	TEST_NAME=$1
1167	BOOT_ISOLATED_FILE=/sys/devices/system/cpu/isolated
1168
1169	[[ -r $BOOT_ISOLATED_FILE ]] || {
1170		echo "$TEST_NAME test SKIPPED: boot isolation state unavailable"
1171		return 1
1172	}
1173	BOOT_CPUS=$(cat $BOOT_ISOLATED_FILE)
1174	[[ -n "$BOOT_CPUS" ]] || {
1175		echo "$TEST_NAME test SKIPPED: no boot-isolated CPU"
1176		return 1
1177	}
1178
1179	BOOT_CPU=$(echo "$BOOT_CPUS" | sed -e 's/[,-].*//')
1180	CPU_ONLINE=/sys/devices/system/cpu/cpu${BOOT_CPU}/online
1181	[[ ! -e $CPU_ONLINE || $(cat $CPU_ONLINE) -eq 1 ]] || {
1182		echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is offline"
1183		return 1
1184	}
1185}
1186
1187#
1188# A CPU isolated at boot must stay isolated after it is released by a dynamic
1189# isolated partition.
1190#
1191test_boot_isolated()
1192{
1193	TEST_NAME="Boot-isolated CPU partition release"
1194	get_boot_isolated_cpu "$TEST_NAME" || return 0
1195	echo "Running $TEST_NAME test ..."
1196
1197	cd $CGROUP2/test
1198	echo member > cpuset.cpus.partition
1199	echo $BOOT_CPU > cpuset.cpus
1200	[[ $(cat cpuset.cpus.effective) = "$BOOT_CPU" ]] || {
1201		echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is unavailable"
1202		echo "" > cpuset.cpus
1203		cd $CGROUP2
1204		return 0
1205	}
1206	test_partition isolated
1207	test_partition member
1208	check_isolcpus "." || {
1209		echo "Boot-isolated CPU $BOOT_CPU was lost after partition release"
1210		exit 1
1211	}
1212	echo "" > cpuset.cpus
1213	cd $CGROUP2
1214	echo "$TEST_NAME test PASSED."
1215}
1216
1217#
1218# Wait for inotify event for the given file and read it
1219# $1: cgroup file to wait for
1220# $2: file to store the read result
1221#
1222wait_inotify()
1223{
1224	CGROUP_FILE=$1
1225	OUTPUT_FILE=$2
1226
1227	$WAIT_INOTIFY $CGROUP_FILE
1228	cat $CGROUP_FILE > $OUTPUT_FILE
1229}
1230
1231#
1232# Test if inotify events are properly generated when going into and out of
1233# invalid partition state.
1234#
1235test_inotify()
1236{
1237	ERR=0
1238	PRS=/tmp/.prs_$$
1239	cd $CGROUP2/test
1240	[[ -f $WAIT_INOTIFY ]] || {
1241		echo "wait_inotify not found, inotify test SKIPPED."
1242		return
1243	}
1244
1245	pause 0.01
1246	echo 1 > cpuset.cpus
1247	echo 0 > cgroup.procs
1248	echo root > cpuset.cpus.partition
1249	pause 0.01
1250	rm -f $PRS
1251	wait_inotify $PWD/cpuset.cpus.partition $PRS &
1252	pause 0.01
1253	set_ctrl_state . "O1=0"
1254	pause 0.01
1255	check_cgroup_states ".:P-1"
1256	if [[ $? -ne 0 ]]
1257	then
1258		echo "FAILED: Inotify test - partition not invalid"
1259		ERR=1
1260	elif [[ ! -f $PRS ]]
1261	then
1262		echo "FAILED: Inotify test - event not generated"
1263		ERR=1
1264		kill %1
1265	elif [[ $(cat $PRS) != "root invalid"* ]]
1266	then
1267		echo "FAILED: Inotify test - incorrect state"
1268		cat $PRS
1269		ERR=1
1270	fi
1271	online_cpus
1272	echo member > cpuset.cpus.partition
1273	echo 0 > ../cgroup.procs
1274	if [[ $ERR -ne 0 ]]
1275	then
1276		exit 1
1277	else
1278		echo "Inotify test PASSED"
1279	fi
1280	echo member > cpuset.cpus.partition
1281	echo "" > cpuset.cpus
1282}
1283
1284trap cleanup 0 2 3 6
1285run_state_test TEST_MATRIX
1286run_remote_state_test REMOTE_TEST_MATRIX
1287test_isolated
1288test_boot_isolated
1289test_inotify
1290echo "All tests PASSED."
1291