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