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 is set, if available, 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 0
14}
15
16[[ $(id -u) -eq 0 ]] || skip_test "Test must be run as root!"
17
18# Set sched verbose flag, if available
19[[ -d /sys/kernel/debug/sched ]] && echo Y > /sys/kernel/debug/sched/verbose
20
21# Get wait_inotify location
22WAIT_INOTIFY=$(cd $(dirname $0); pwd)/wait_inotify
23
24# Find cgroup v2 mount point
25CGROUP2=$(mount -t cgroup2 | head -1 | awk -e '{print $3}')
26[[ -n "$CGROUP2" ]] || skip_test "Cgroup v2 mount point not found!"
27
28CPUS=$(lscpu | grep "^CPU(s)" | sed -e "s/.*:[[:space:]]*//")
29[[ $CPUS -lt 8 ]] && skip_test "Test needs at least 8 cpus available!"
30
31# Set verbose flag and delay factor
32PROG=$1
33VERBOSE=
34DELAY_FACTOR=1
35while [[ "$1" = -* ]]
36do
37	case "$1" in
38		-v) VERBOSE=1
39		    break
40		    ;;
41		-d) DELAY_FACTOR=$2
42		    shift
43		    break
44		    ;;
45		*)  echo "Usage: $PROG [-v] [-d <delay-factor>"
46		    exit
47		    ;;
48	esac
49	shift
50done
51
52cd $CGROUP2
53echo +cpuset > cgroup.subtree_control
54[[ -d test ]] || mkdir test
55cd test
56
57# Pause in ms
58pause()
59{
60	DELAY=$1
61	LOOP=0
62	while [[ $LOOP -lt $DELAY_FACTOR ]]
63	do
64		sleep $DELAY
65		((LOOP++))
66	done
67	return 0
68}
69
70console_msg()
71{
72	MSG=$1
73	echo "$MSG"
74	echo "" > /dev/console
75	echo "$MSG" > /dev/console
76	pause 0.01
77}
78
79test_partition()
80{
81	EXPECTED_VAL=$1
82	echo $EXPECTED_VAL > cpuset.cpus.partition
83	[[ $? -eq 0 ]] || exit 1
84	ACTUAL_VAL=$(cat cpuset.cpus.partition)
85	[[ $ACTUAL_VAL != $EXPECTED_VAL ]] && {
86		echo "cpuset.cpus.partition: expect $EXPECTED_VAL, found $EXPECTED_VAL"
87		echo "Test FAILED"
88		exit 1
89	}
90}
91
92test_effective_cpus()
93{
94	EXPECTED_VAL=$1
95	ACTUAL_VAL=$(cat cpuset.cpus.effective)
96	[[ "$ACTUAL_VAL" != "$EXPECTED_VAL" ]] && {
97		echo "cpuset.cpus.effective: expect '$EXPECTED_VAL', found '$EXPECTED_VAL'"
98		echo "Test FAILED"
99		exit 1
100	}
101}
102
103# Adding current process to cgroup.procs as a test
104test_add_proc()
105{
106	OUTSTR="$1"
107	ERRMSG=$((echo $$ > cgroup.procs) |& cat)
108	echo $ERRMSG | grep -q "$OUTSTR"
109	[[ $? -ne 0 ]] && {
110		echo "cgroup.procs: expect '$OUTSTR', got '$ERRMSG'"
111		echo "Test FAILED"
112		exit 1
113	}
114	echo $$ > $CGROUP2/cgroup.procs	# Move out the task
115}
116
117#
118# Testing the new "isolated" partition root type
119#
120test_isolated()
121{
122	echo 2-3 > cpuset.cpus
123	TYPE=$(cat cpuset.cpus.partition)
124	[[ $TYPE = member ]] || echo member > cpuset.cpus.partition
125
126	console_msg "Change from member to root"
127	test_partition root
128
129	console_msg "Change from root to isolated"
130	test_partition isolated
131
132	console_msg "Change from isolated to member"
133	test_partition member
134
135	console_msg "Change from member to isolated"
136	test_partition isolated
137
138	console_msg "Change from isolated to root"
139	test_partition root
140
141	console_msg "Change from root to member"
142	test_partition member
143
144	#
145	# Testing partition root with no cpu
146	#
147	console_msg "Distribute all cpus to child partition"
148	echo +cpuset > cgroup.subtree_control
149	test_partition root
150
151	mkdir A1
152	cd A1
153	echo 2-3 > cpuset.cpus
154	test_partition root
155	test_effective_cpus 2-3
156	cd ..
157	test_effective_cpus ""
158
159	console_msg "Moving task to partition test"
160	test_add_proc "No space left"
161	cd A1
162	test_add_proc ""
163	cd ..
164
165	console_msg "Shrink and expand child partition"
166	cd A1
167	echo 2 > cpuset.cpus
168	cd ..
169	test_effective_cpus 3
170	cd A1
171	echo 2-3 > cpuset.cpus
172	cd ..
173	test_effective_cpus ""
174
175	# Cleaning up
176	console_msg "Cleaning up"
177	echo $$ > $CGROUP2/cgroup.procs
178	[[ -d A1 ]] && rmdir A1
179}
180
181#
182# Cpuset controller state transition test matrix.
183#
184# Cgroup test hierarchy
185#
186# test -- A1 -- A2 -- A3
187#      \- B1
188#
189#  P<v> = set cpus.partition (0:member, 1:root, 2:isolated, -1:root invalid)
190#  C<l> = add cpu-list
191#  S<p> = use prefix in subtree_control
192#  T    = put a task into cgroup
193#  O<c>-<v> = Write <v> to CPU online file of <c>
194#
195SETUP_A123_PARTITIONS="C1-3:P1:S+ C2-3:P1:S+ C3:P1"
196TEST_MATRIX=(
197	# test  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate
198	# ----  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------
199	"  S+    C0-1     .      .    C2-3    S+    C4-5     .      .     0 A2:0-1"
200	"  S+    C0-1     .      .    C2-3    P1      .      .      .     0 "
201	"  S+    C0-1     .      .    C2-3   P1:S+ C0-1:P1   .      .     0 "
202	"  S+    C0-1     .      .    C2-3   P1:S+  C1:P1    .      .     0 "
203	"  S+   C0-1:S+   .      .    C2-3     .      .      .     P1     0 "
204	"  S+   C0-1:P1   .      .    C2-3    S+     C1      .      .     0 "
205	"  S+   C0-1:P1   .      .    C2-3    S+    C1:P1    .      .     0 "
206	"  S+   C0-1:P1   .      .    C2-3    S+    C1:P1    .     P1     0 "
207	"  S+   C0-1:P1   .      .    C2-3   C4-5     .      .      .     0 A1:4-5"
208	"  S+   C0-1:P1   .      .    C2-3  S+:C4-5   .      .      .     0 A1:4-5"
209	"  S+    C0-1     .      .   C2-3:P1   .      .      .     C2     0 "
210	"  S+    C0-1     .      .   C2-3:P1   .      .      .    C4-5    0 B1:4-5"
211	"  S+ C0-3:P1:S+ C2-3:P1 .      .      .      .      .      .     0 A1:0-1,A2:2-3"
212	"  S+ C0-3:P1:S+ C2-3:P1 .      .     C1-3    .      .      .     0 A1:1,A2:2-3"
213	"  S+ C2-3:P1:S+  C3:P1  .      .     C3      .      .      .     0 A1:,A2:3 A1:P1,A2:P1"
214	"  S+ C2-3:P1:S+  C3:P1  .      .     C3      P0     .      .     0 A1:3,A2:3 A1:P1,A2:P0"
215	"  S+ C2-3:P1:S+  C2:P1  .      .     C2-4    .      .      .     0 A1:3-4,A2:2"
216	"  S+ C2-3:P1:S+  C3:P1  .      .     C3      .      .     C0-2   0 A1:,B1:0-2 A1:P1,A2:P1"
217	"  S+ $SETUP_A123_PARTITIONS    .     C2-3    .      .      .     0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
218
219	# CPU offlining cases:
220	"  S+    C0-1     .      .    C2-3    S+    C4-5     .     O2-0   0 A1:0-1,B1:3"
221	"  S+ C0-3:P1:S+ C2-3:P1 .      .     O2-0    .      .      .     0 A1:0-1,A2:3"
222	"  S+ C0-3:P1:S+ C2-3:P1 .      .     O2-0   O2-1    .      .     0 A1:0-1,A2:2-3"
223	"  S+ C0-3:P1:S+ C2-3:P1 .      .     O1-0    .      .      .     0 A1:0,A2:2-3"
224	"  S+ C0-3:P1:S+ C2-3:P1 .      .     O1-0   O1-1    .      .     0 A1:0-1,A2:2-3"
225	"  S+ C2-3:P1:S+  C3:P1  .      .     O3-0   O3-1    .      .     0 A1:2,A2:3 A1:P1,A2:P1"
226	"  S+ C2-3:P1:S+  C3:P2  .      .     O3-0   O3-1    .      .     0 A1:2,A2:3 A1:P1,A2:P2"
227	"  S+ C2-3:P1:S+  C3:P1  .      .     O2-0   O2-1    .      .     0 A1:2,A2:3 A1:P1,A2:P1"
228	"  S+ C2-3:P1:S+  C3:P2  .      .     O2-0   O2-1    .      .     0 A1:2,A2:3 A1:P1,A2:P2"
229	"  S+ C2-3:P1:S+  C3:P1  .      .     O2-0    .      .      .     0 A1:,A2:3 A1:P1,A2:P1"
230	"  S+ C2-3:P1:S+  C3:P1  .      .     O3-0    .      .      .     0 A1:2,A2: A1:P1,A2:P1"
231	"  S+ C2-3:P1:S+  C3:P1  .      .    T:O2-0   .      .      .     0 A1:3,A2:3 A1:P1,A2:P-1"
232	"  S+ C2-3:P1:S+  C3:P1  .      .      .    T:O3-0   .      .     0 A1:2,A2:2 A1:P1,A2:P-1"
233	"  S+ $SETUP_A123_PARTITIONS    .     O1-0    .      .      .     0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
234	"  S+ $SETUP_A123_PARTITIONS    .     O2-0    .      .      .     0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1"
235	"  S+ $SETUP_A123_PARTITIONS    .     O3-0    .      .      .     0 A1:1,A2:2,A3: A1:P1,A2:P1,A3:P1"
236	"  S+ $SETUP_A123_PARTITIONS    .    T:O1-0   .      .      .     0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1"
237	"  S+ $SETUP_A123_PARTITIONS    .      .    T:O2-0   .      .     0 A1:1,A2:3,A3:3 A1:P1,A2:P1,A3:P-1"
238	"  S+ $SETUP_A123_PARTITIONS    .      .      .    T:O3-0   .     0 A1:1,A2:2,A3:2 A1:P1,A2:P1,A3:P-1"
239	"  S+ $SETUP_A123_PARTITIONS    .    T:O1-0  O1-1    .      .     0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
240	"  S+ $SETUP_A123_PARTITIONS    .      .    T:O2-0  O2-1    .     0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
241	"  S+ $SETUP_A123_PARTITIONS    .      .      .    T:O3-0  O3-1   0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
242	"  S+ $SETUP_A123_PARTITIONS    .    T:O1-0  O2-0   O1-1    .     0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1"
243	"  S+ $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"
244
245	# test  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate
246	# ----  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------
247	#
248	# Incorrect change to cpuset.cpus invalidates partition root
249	#
250	# Adding CPUs to partition root that are not in parent's
251	# cpuset.cpus is allowed, but those extra CPUs are ignored.
252	"  S+ C2-3:P1:S+ C3:P1   .      .      .     C2-4    .      .     0 A1:,A2:2-3 A1:P1,A2:P1"
253
254	# Taking away all CPUs from parent or itself if there are tasks
255	# will make the partition invalid.
256	"  S+ C2-3:P1:S+  C3:P1  .      .      T     C2-3    .      .     0 A1:2-3,A2:2-3 A1:P1,A2:P-1"
257	"  S+ $SETUP_A123_PARTITIONS    .    T:C2-3   .      .      .     0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1"
258	"  S+ $SETUP_A123_PARTITIONS    . T:C2-3:C1-3 .      .      .     0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
259
260	# Changing a partition root to member makes child partitions invalid
261	"  S+ C2-3:P1:S+  C3:P1  .      .      P0     .      .      .     0 A1:2-3,A2:3 A1:P0,A2:P-1"
262	"  S+ $SETUP_A123_PARTITIONS    .     C2-3    P0     .      .     0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P0,A3:P-1"
263
264	# cpuset.cpus can contains cpus not in parent's cpuset.cpus as long
265	# as they overlap.
266	"  S+ C2-3:P1:S+  .      .      .      .   C3-4:P1   .      .     0 A1:2,A2:3 A1:P1,A2:P1"
267
268	# Deletion of CPUs distributed to child cgroup is allowed.
269	"  S+ C0-1:P1:S+ C1      .    C2-3   C4-5     .      .      .     0 A1:4-5,A2:4-5"
270
271	# To become a valid partition root, cpuset.cpus must overlap parent's
272	# cpuset.cpus.
273	"  S+   C0-1:P1   .      .    C2-3    S+   C4-5:P1   .      .     0 A1:0-1,A2:0-1 A1:P1,A2:P-1"
274
275	# Enabling partition with child cpusets is allowed
276	"  S+   C0-1:S+  C1      .    C2-3    P1      .      .      .     0 A1:0-1,A2:1 A1:P1"
277
278	# A partition root with non-partition root parent is invalid, but it
279	# can be made valid if its parent becomes a partition root too.
280	"  S+   C0-1:S+  C1      .    C2-3     .      P2     .      .     0 A1:0-1,A2:1 A1:P0,A2:P-2"
281	"  S+   C0-1:S+ C1:P2    .    C2-3     P1     .      .      .     0 A1:0,A2:1 A1:P1,A2:P2"
282
283	# A non-exclusive cpuset.cpus change will invalidate partition and its siblings
284	"  S+   C0-1:P1   .      .    C2-3   C0-2     .      .      .     0 A1:0-2,B1:2-3 A1:P-1,B1:P0"
285	"  S+   C0-1:P1   .      .  P1:C2-3  C0-2   .      .      .     0 A1:0-2,B1:2-3 A1:P-1,B1:P-1"
286	"  S+    C0-1     .      .  P1:C2-3  C0-2   .      .      .     0 A1:0-2,B1:2-3 A1:P0,B1:P-1"
287
288	# test  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate
289	# ----  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------
290	# Failure cases:
291
292	# A task cannot be added to a partition with no cpu
293	"  S+ C2-3:P1:S+  C3:P1  .      .    O2-0:T   .      .      .     1 A1:,A2:3 A1:P1,A2:P1"
294)
295
296#
297# Write to the cpu online file
298#  $1 - <c>-<v> where <c> = cpu number, <v> value to be written
299#
300write_cpu_online()
301{
302	CPU=${1%-*}
303	VAL=${1#*-}
304	CPUFILE=//sys/devices/system/cpu/cpu${CPU}/online
305	if [[ $VAL -eq 0 ]]
306	then
307		OFFLINE_CPUS="$OFFLINE_CPUS $CPU"
308	else
309		[[ -n "$OFFLINE_CPUS" ]] && {
310			OFFLINE_CPUS=$(echo $CPU $CPU $OFFLINE_CPUS | fmt -1 |\
311					sort | uniq -u)
312		}
313	fi
314	echo $VAL > $CPUFILE
315	pause 0.01
316}
317
318#
319# Set controller state
320#  $1 - cgroup directory
321#  $2 - state
322#  $3 - showerr
323#
324# The presence of ":" in state means transition from one to the next.
325#
326set_ctrl_state()
327{
328	TMPMSG=/tmp/.msg_$$
329	CGRP=$1
330	STATE=$2
331	SHOWERR=${3}${VERBOSE}
332	CTRL=${CTRL:=$CONTROLLER}
333	HASERR=0
334	REDIRECT="2> $TMPMSG"
335	[[ -z "$STATE" || "$STATE" = '.' ]] && return 0
336
337	rm -f $TMPMSG
338	for CMD in $(echo $STATE | sed -e "s/:/ /g")
339	do
340		TFILE=$CGRP/cgroup.procs
341		SFILE=$CGRP/cgroup.subtree_control
342		PFILE=$CGRP/cpuset.cpus.partition
343		CFILE=$CGRP/cpuset.cpus
344		S=$(expr substr $CMD 1 1)
345		if [[ $S = S ]]
346		then
347			PREFIX=${CMD#?}
348			COMM="echo ${PREFIX}${CTRL} > $SFILE"
349			eval $COMM $REDIRECT
350		elif [[ $S = C ]]
351		then
352			CPUS=${CMD#?}
353			COMM="echo $CPUS > $CFILE"
354			eval $COMM $REDIRECT
355		elif [[ $S = P ]]
356		then
357			VAL=${CMD#?}
358			case $VAL in
359			0)  VAL=member
360			    ;;
361			1)  VAL=root
362			    ;;
363			2)  VAL=isolated
364			    ;;
365			*)
366			    echo "Invalid partition state - $VAL"
367			    exit 1
368			    ;;
369			esac
370			COMM="echo $VAL > $PFILE"
371			eval $COMM $REDIRECT
372		elif [[ $S = O ]]
373		then
374			VAL=${CMD#?}
375			write_cpu_online $VAL
376		elif [[ $S = T ]]
377		then
378			COMM="echo 0 > $TFILE"
379			eval $COMM $REDIRECT
380		fi
381		RET=$?
382		[[ $RET -ne 0 ]] && {
383			[[ -n "$SHOWERR" ]] && {
384				echo "$COMM"
385				cat $TMPMSG
386			}
387			HASERR=1
388		}
389		pause 0.01
390		rm -f $TMPMSG
391	done
392	return $HASERR
393}
394
395set_ctrl_state_noerr()
396{
397	CGRP=$1
398	STATE=$2
399	[[ -d $CGRP ]] || mkdir $CGRP
400	set_ctrl_state $CGRP $STATE 1
401	[[ $? -ne 0 ]] && {
402		echo "ERROR: Failed to set $2 to cgroup $1!"
403		exit 1
404	}
405}
406
407online_cpus()
408{
409	[[ -n "OFFLINE_CPUS" ]] && {
410		for C in $OFFLINE_CPUS
411		do
412			write_cpu_online ${C}-1
413		done
414	}
415}
416
417#
418# Return 1 if the list of effective cpus isn't the same as the initial list.
419#
420reset_cgroup_states()
421{
422	echo 0 > $CGROUP2/cgroup.procs
423	online_cpus
424	rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1
425	set_ctrl_state . S-
426	pause 0.01
427}
428
429dump_states()
430{
431	for DIR in A1 A1/A2 A1/A2/A3 B1
432	do
433		ECPUS=$DIR/cpuset.cpus.effective
434		PRS=$DIR/cpuset.cpus.partition
435		[[ -e $ECPUS ]] && echo "$ECPUS: $(cat $ECPUS)"
436		[[ -e $PRS   ]] && echo "$PRS: $(cat $PRS)"
437	done
438}
439
440#
441# Check effective cpus
442# $1 - check string, format: <cgroup>:<cpu-list>[,<cgroup>:<cpu-list>]*
443#
444check_effective_cpus()
445{
446	CHK_STR=$1
447	for CHK in $(echo $CHK_STR | sed -e "s/,/ /g")
448	do
449		set -- $(echo $CHK | sed -e "s/:/ /g")
450		CGRP=$1
451		CPUS=$2
452		[[ $CGRP = A2 ]] && CGRP=A1/A2
453		[[ $CGRP = A3 ]] && CGRP=A1/A2/A3
454		FILE=$CGRP/cpuset.cpus.effective
455		[[ -e $FILE ]] || return 1
456		[[ $CPUS = $(cat $FILE) ]] || return 1
457	done
458}
459
460#
461# Check cgroup states
462#  $1 - check string, format: <cgroup>:<state>[,<cgroup>:<state>]*
463#
464check_cgroup_states()
465{
466	CHK_STR=$1
467	for CHK in $(echo $CHK_STR | sed -e "s/,/ /g")
468	do
469		set -- $(echo $CHK | sed -e "s/:/ /g")
470		CGRP=$1
471		STATE=$2
472		FILE=
473		EVAL=$(expr substr $STATE 2 2)
474		[[ $CGRP = A2 ]] && CGRP=A1/A2
475		[[ $CGRP = A3 ]] && CGRP=A1/A2/A3
476
477		case $STATE in
478			P*) FILE=$CGRP/cpuset.cpus.partition
479			    ;;
480			*)  echo "Unknown state: $STATE!"
481			    exit 1
482			    ;;
483		esac
484		VAL=$(cat $FILE)
485
486		case "$VAL" in
487			member) VAL=0
488				;;
489			root)	VAL=1
490				;;
491			isolated)
492				VAL=2
493				;;
494			"root invalid"*)
495				VAL=-1
496				;;
497			"isolated invalid"*)
498				VAL=-2
499				;;
500		esac
501		[[ $EVAL != $VAL ]] && return 1
502	done
503	return 0
504}
505
506#
507# Run cpuset state transition test
508#  $1 - test matrix name
509#
510# This test is somewhat fragile as delays (sleep x) are added in various
511# places to make sure state changes are fully propagated before the next
512# action. These delays may need to be adjusted if running in a slower machine.
513#
514run_state_test()
515{
516	TEST=$1
517	CONTROLLER=cpuset
518	CPULIST=0-6
519	I=0
520	eval CNT="\${#$TEST[@]}"
521
522	reset_cgroup_states
523	echo $CPULIST > cpuset.cpus
524	echo root > cpuset.cpus.partition
525	console_msg "Running state transition test ..."
526
527	while [[ $I -lt $CNT ]]
528	do
529		echo "Running test $I ..." > /dev/console
530		eval set -- "\${$TEST[$I]}"
531		ROOT=$1
532		OLD_A1=$2
533		OLD_A2=$3
534		OLD_A3=$4
535		OLD_B1=$5
536		NEW_A1=$6
537		NEW_A2=$7
538		NEW_A3=$8
539		NEW_B1=$9
540		RESULT=${10}
541		ECPUS=${11}
542		STATES=${12}
543
544		set_ctrl_state_noerr .        $ROOT
545		set_ctrl_state_noerr A1       $OLD_A1
546		set_ctrl_state_noerr A1/A2    $OLD_A2
547		set_ctrl_state_noerr A1/A2/A3 $OLD_A3
548		set_ctrl_state_noerr B1       $OLD_B1
549		RETVAL=0
550		set_ctrl_state A1       $NEW_A1; ((RETVAL += $?))
551		set_ctrl_state A1/A2    $NEW_A2; ((RETVAL += $?))
552		set_ctrl_state A1/A2/A3 $NEW_A3; ((RETVAL += $?))
553		set_ctrl_state B1       $NEW_B1; ((RETVAL += $?))
554
555		[[ $RETVAL -ne $RESULT ]] && {
556			echo "Test $TEST[$I] failed result check!"
557			eval echo \"\${$TEST[$I]}\"
558			dump_states
559			online_cpus
560			exit 1
561		}
562
563		[[ -n "$ECPUS" && "$ECPUS" != . ]] && {
564			check_effective_cpus $ECPUS
565			[[ $? -ne 0 ]] && {
566				echo "Test $TEST[$I] failed effective CPU check!"
567				eval echo \"\${$TEST[$I]}\"
568				echo
569				dump_states
570				online_cpus
571				exit 1
572			}
573		}
574
575		[[ -n "$STATES" ]] && {
576			check_cgroup_states $STATES
577			[[ $? -ne 0 ]] && {
578				echo "FAILED: Test $TEST[$I] failed states check!"
579				eval echo \"\${$TEST[$I]}\"
580				echo
581				dump_states
582				online_cpus
583				exit 1
584			}
585		}
586
587		reset_cgroup_states
588		#
589		# Check to see if effective cpu list changes
590		#
591		pause 0.05
592		NEWLIST=$(cat cpuset.cpus.effective)
593		[[ $NEWLIST != $CPULIST ]] && {
594			echo "Effective cpus changed to $NEWLIST after test $I!"
595			exit 1
596		}
597		[[ -n "$VERBOSE" ]] && echo "Test $I done."
598		((I++))
599	done
600	echo "All $I tests of $TEST PASSED."
601
602	echo member > cpuset.cpus.partition
603}
604
605#
606# Wait for inotify event for the given file and read it
607# $1: cgroup file to wait for
608# $2: file to store the read result
609#
610wait_inotify()
611{
612	CGROUP_FILE=$1
613	OUTPUT_FILE=$2
614
615	$WAIT_INOTIFY $CGROUP_FILE
616	cat $CGROUP_FILE > $OUTPUT_FILE
617}
618
619#
620# Test if inotify events are properly generated when going into and out of
621# invalid partition state.
622#
623test_inotify()
624{
625	ERR=0
626	PRS=/tmp/.prs_$$
627	[[ -f $WAIT_INOTIFY ]] || {
628		echo "wait_inotify not found, inotify test SKIPPED."
629		return
630	}
631
632	pause 0.01
633	echo 1 > cpuset.cpus
634	echo 0 > cgroup.procs
635	echo root > cpuset.cpus.partition
636	pause 0.01
637	rm -f $PRS
638	wait_inotify $PWD/cpuset.cpus.partition $PRS &
639	pause 0.01
640	set_ctrl_state . "O1-0"
641	pause 0.01
642	check_cgroup_states ".:P-1"
643	if [[ $? -ne 0 ]]
644	then
645		echo "FAILED: Inotify test - partition not invalid"
646		ERR=1
647	elif [[ ! -f $PRS ]]
648	then
649		echo "FAILED: Inotify test - event not generated"
650		ERR=1
651		kill %1
652	elif [[ $(cat $PRS) != "root invalid"* ]]
653	then
654		echo "FAILED: Inotify test - incorrect state"
655		cat $PRS
656		ERR=1
657	fi
658	online_cpus
659	echo member > cpuset.cpus.partition
660	echo 0 > ../cgroup.procs
661	if [[ $ERR -ne 0 ]]
662	then
663		exit 1
664	else
665		echo "Inotify test PASSED"
666	fi
667}
668
669run_state_test TEST_MATRIX
670test_isolated
671test_inotify
672echo "All tests PASSED."
673cd ..
674rmdir test
675