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