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