Merge tag 'i3c/for-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux
[linux-block.git] / tools / testing / selftests / cgroup / test_cpuset_prs.sh
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
10 skip_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
20 WAIT_INOTIFY=$(cd $(dirname $0); pwd)/wait_inotify
21
22 # Find cgroup v2 mount point
23 CGROUP2=$(mount -t cgroup2 | head -1 | awk -e '{print $3}')
24 [[ -n "$CGROUP2" ]] || skip_test "Cgroup v2 mount point not found!"
25 SUBPARTS_CPUS=$CGROUP2/.__DEBUG__.cpuset.cpus.subpartitions
26 CPULIST=$(cat $CGROUP2/cpuset.cpus.effective)
27
28 NR_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 # Set verbose flag and delay factor
32 PROG=$1
33 VERBOSE=0
34 DELAY_FACTOR=1
35 SCHED_DEBUG=
36 while [[ "$1" = -* ]]
37 do
38         case "$1" in
39                 -v) ((VERBOSE++))
40                     # Enable sched/verbose can slow thing down
41                     [[ $DELAY_FACTOR -eq 1 ]] &&
42                         DELAY_FACTOR=2
43                     ;;
44                 -d) DELAY_FACTOR=$2
45                     shift
46                     ;;
47                 *)  echo "Usage: $PROG [-v] [-d <delay-factor>"
48                     exit
49                     ;;
50         esac
51         shift
52 done
53
54 # Set sched verbose flag if available when "-v" option is specified
55 if [[ $VERBOSE -gt 0 && -d /sys/kernel/debug/sched ]]
56 then
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
60 fi
61
62 cd $CGROUP2
63 echo +cpuset > cgroup.subtree_control
64
65 #
66 # If cpuset has been set up and used in child cgroups, we may not be able to
67 # create partition under root cgroup because of the CPU exclusivity rule.
68 # So we are going to skip the test if this is the case.
69 #
70 [[ -d test ]] || mkdir test
71 echo 0-6 > test/cpuset.cpus
72 echo root > test/cpuset.cpus.partition
73 cat test/cpuset.cpus.partition | grep -q invalid
74 RESULT=$?
75 echo member > test/cpuset.cpus.partition
76 echo "" > test/cpuset.cpus
77 [[ $RESULT -eq 0 ]] && skip_test "Child cgroups are using cpuset!"
78
79 cleanup()
80 {
81         online_cpus
82         cd $CGROUP2
83         rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1
84         rmdir test > /dev/null 2>&1
85         [[ -n "$SCHED_DEBUG" ]] &&
86                 echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose
87 }
88
89 # Pause in ms
90 pause()
91 {
92         DELAY=$1
93         LOOP=0
94         while [[ $LOOP -lt $DELAY_FACTOR ]]
95         do
96                 sleep $DELAY
97                 ((LOOP++))
98         done
99         return 0
100 }
101
102 console_msg()
103 {
104         MSG=$1
105         echo "$MSG"
106         echo "" > /dev/console
107         echo "$MSG" > /dev/console
108         pause 0.01
109 }
110
111 test_partition()
112 {
113         EXPECTED_VAL=$1
114         echo $EXPECTED_VAL > cpuset.cpus.partition
115         [[ $? -eq 0 ]] || exit 1
116         ACTUAL_VAL=$(cat cpuset.cpus.partition)
117         [[ $ACTUAL_VAL != $EXPECTED_VAL ]] && {
118                 echo "cpuset.cpus.partition: expect $EXPECTED_VAL, found $ACTUAL_VAL"
119                 echo "Test FAILED"
120                 exit 1
121         }
122 }
123
124 test_effective_cpus()
125 {
126         EXPECTED_VAL=$1
127         ACTUAL_VAL=$(cat cpuset.cpus.effective)
128         [[ "$ACTUAL_VAL" != "$EXPECTED_VAL" ]] && {
129                 echo "cpuset.cpus.effective: expect '$EXPECTED_VAL', found '$ACTUAL_VAL'"
130                 echo "Test FAILED"
131                 exit 1
132         }
133 }
134
135 # Adding current process to cgroup.procs as a test
136 test_add_proc()
137 {
138         OUTSTR="$1"
139         ERRMSG=$((echo $$ > cgroup.procs) |& cat)
140         echo $ERRMSG | grep -q "$OUTSTR"
141         [[ $? -ne 0 ]] && {
142                 echo "cgroup.procs: expect '$OUTSTR', got '$ERRMSG'"
143                 echo "Test FAILED"
144                 exit 1
145         }
146         echo $$ > $CGROUP2/cgroup.procs # Move out the task
147 }
148
149 #
150 # Cpuset controller state transition test matrix.
151 #
152 # Cgroup test hierarchy
153 #
154 # root -- A1 -- A2 -- A3
155 #      +- B1
156 #
157 #  P<v> = set cpus.partition (0:member, 1:root, 2:isolated)
158 #  C<l> = add cpu-list to cpuset.cpus
159 #  X<l> = add cpu-list to cpuset.cpus.exclusive
160 #  S<p> = use prefix in subtree_control
161 #  T    = put a task into cgroup
162 #  O<c>=<v> = Write <v> to CPU online file of <c>
163 #
164 SETUP_A123_PARTITIONS="C1-3:P1:S+ C2-3:P1:S+ C3:P1"
165 TEST_MATRIX=(
166         #  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
167         #  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
168         "   C0-1     .      .    C2-3    S+    C4-5     .      .     0 A2:0-1"
169         "   C0-1     .      .    C2-3    P1      .      .      .     0 "
170         "   C0-1     .      .    C2-3   P1:S+ C0-1:P1   .      .     0 "
171         "   C0-1     .      .    C2-3   P1:S+  C1:P1    .      .     0 "
172         "  C0-1:S+   .      .    C2-3     .      .      .     P1     0 "
173         "  C0-1:P1   .      .    C2-3    S+     C1      .      .     0 "
174         "  C0-1:P1   .      .    C2-3    S+    C1:P1    .      .     0 "
175         "  C0-1:P1   .      .    C2-3    S+    C1:P1    .     P1     0 "
176         "  C0-1:P1   .      .    C2-3   C4-5     .      .      .     0 A1:4-5"
177         "  C0-1:P1   .      .    C2-3  S+:C4-5   .      .      .     0 A1:4-5"
178         "   C0-1     .      .   C2-3:P1   .      .      .     C2     0 "
179         "   C0-1     .      .   C2-3:P1   .      .      .    C4-5    0 B1:4-5"
180         "C0-3:P1:S+ C2-3:P1 .      .      .      .      .      .     0 A1:0-1,A2:2-3"
181         "C0-3:P1:S+ C2-3:P1 .      .     C1-3    .      .      .     0 A1:1,A2:2-3"
182         "C2-3:P1:S+  C3:P1  .      .     C3      .      .      .     0 A1:,A2:3 A1:P1,A2:P1"
183         "C2-3:P1:S+  C3:P1  .      .     C3      P0     .      .     0 A1:3,A2:3 A1:P1,A2:P0"
184         "C2-3:P1:S+  C2:P1  .      .     C2-4    .      .      .     0 A1:3-4,A2:2"
185         "C2-3:P1:S+  C3:P1  .      .     C3      .      .     C0-2   0 A1:,B1:0-2 A1:P1,A2:P1"
186         "$SETUP_A123_PARTITIONS    .     C2-3    .      .      .     0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
187
188         # CPU offlining cases:
189         "   C0-1     .      .    C2-3    S+    C4-5     .     O2=0   0 A1:0-1,B1:3"
190         "C0-3:P1:S+ C2-3:P1 .      .     O2=0    .      .      .     0 A1:0-1,A2:3"
191         "C0-3:P1:S+ C2-3:P1 .      .     O2=0   O2=1    .      .     0 A1:0-1,A2:2-3"
192         "C0-3:P1:S+ C2-3:P1 .      .     O1=0    .      .      .     0 A1:0,A2:2-3"
193         "C0-3:P1:S+ C2-3:P1 .      .     O1=0   O1=1    .      .     0 A1:0-1,A2:2-3"
194         "C2-3:P1:S+  C3:P1  .      .     O3=0   O3=1    .      .     0 A1:2,A2:3 A1:P1,A2:P1"
195         "C2-3:P1:S+  C3:P2  .      .     O3=0   O3=1    .      .     0 A1:2,A2:3 A1:P1,A2:P2"
196         "C2-3:P1:S+  C3:P1  .      .     O2=0   O2=1    .      .     0 A1:2,A2:3 A1:P1,A2:P1"
197         "C2-3:P1:S+  C3:P2  .      .     O2=0   O2=1    .      .     0 A1:2,A2:3 A1:P1,A2:P2"
198         "C2-3:P1:S+  C3:P1  .      .     O2=0    .      .      .     0 A1:,A2:3 A1:P1,A2:P1"
199         "C2-3:P1:S+  C3:P1  .      .     O3=0    .      .      .     0 A1:2,A2: A1:P1,A2:P1"
200         "C2-3:P1:S+  C3:P1  .      .    T:O2=0   .      .      .     0 A1:3,A2:3 A1:P1,A2:P-1"
201         "C2-3:P1:S+  C3:P1  .      .      .    T:O3=0   .      .     0 A1:2,A2:2 A1:P1,A2:P-1"
202         "$SETUP_A123_PARTITIONS    .     O1=0    .      .      .     0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
203         "$SETUP_A123_PARTITIONS    .     O2=0    .      .      .     0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1"
204         "$SETUP_A123_PARTITIONS    .     O3=0    .      .      .     0 A1:1,A2:2,A3: A1:P1,A2:P1,A3:P1"
205         "$SETUP_A123_PARTITIONS    .    T:O1=0   .      .      .     0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1"
206         "$SETUP_A123_PARTITIONS    .      .    T:O2=0   .      .     0 A1:1,A2:3,A3:3 A1:P1,A2:P1,A3:P-1"
207         "$SETUP_A123_PARTITIONS    .      .      .    T:O3=0   .     0 A1:1,A2:2,A3:2 A1:P1,A2:P1,A3:P-1"
208         "$SETUP_A123_PARTITIONS    .    T:O1=0  O1=1    .      .     0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
209         "$SETUP_A123_PARTITIONS    .      .    T:O2=0  O2=1    .     0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
210         "$SETUP_A123_PARTITIONS    .      .      .    T:O3=0  O3=1   0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
211         "$SETUP_A123_PARTITIONS    .    T:O1=0  O2=0   O1=1    .     0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1"
212         "$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"
213
214         #  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
215         #  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
216         #
217         # Remote partition and cpuset.cpus.exclusive tests
218         #
219         " C0-3:S+ C1-3:S+ C2-3     .    X2-3     .      .      .     0 A1:0-3,A2:1-3,A3:2-3,XA1:2-3"
220         " 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"
221         " 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"
222         " 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"
223         " C0-3:S+ C1-3:S+ C2-3     .    X2-3   X2-3 X2-3:P2:C3 .     0 A1:0-2,A2:1-2,A3:3 A1:P0,A3:P2 3"
224         " C0-3:S+ C1-3:S+ C2-3   C2-3     .      .      .      P2    0 A1:0-3,A2:1-3,A3:2-3,B1:2-3 A1:P0,A3:P0,B1:P-2"
225         " C0-3:S+ C1-3:S+ C2-3   C4-5     .      .      .      P2    0 B1:4-5 B1:P2 4-5"
226         " 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"
227         " 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"
228         " 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"
229         " 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"
230
231         # Nested remote/local partition tests
232         " 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 \
233                                                                        A1:P0,A2:P1,A3:P2,B1:P1 2-3"
234         " 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 \
235                                                                        A1:P0,A2:P1,A3:P2,B1:P1 2-4,2-3"
236         " 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 \
237                                                                        A1:P0,A2:P1,A3:P2,B1:P1 2-4,3"
238         " C0-4:S+ C1-4:S+ C2-4     .    X2-4  X2-4:P2  X4:P1    .    0 A1:0-1,A2:2-3,A3:4 \
239                                                                        A1:P0,A2:P2,A3:P1 2-4,2-3"
240         " C0-4:X2-4:S+ C1-4:X2-4:S+:P2 C2-4:X4:P1 \
241                                    .      .      X5      .      .    0 A1:0-4,A2:1-4,A3:2-4 \
242                                                                        A1:P0,A2:P-2,A3:P-1"
243         " C0-4:X2-4:S+ C1-4:X2-4:S+:P2 C2-4:X4:P1 \
244                                    .      .      .      X1      .    0 A1:0-1,A2:2-4,A3:2-4 \
245                                                                        A1:P0,A2:P2,A3:P-1 2-4"
246
247         # Remote partition offline tests
248         " 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"
249         " 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"
250         " 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"
251         " 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,"
252
253         # An invalidated remote partition cannot self-recover from hotplug
254         " 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"
255
256         # cpus.exclusive.effective clearing test
257         " C0-3:S+ C1-3:S+  C2      .   X2-3:X    .      .      .     0 A1:0-3,A2:1-3,A3:2,XA1:"
258
259         # Invalid to valid remote partition transition test
260         " C0-3:S+   C1-3    .      .      .    X3:P2    .      .     0 A1:0-3,A2:1-3,XA2: A2:P-2"
261         " C0-3:S+ C1-3:X3:P2
262                             .      .    X2-3    P2      .      .     0 A1:0-2,A2:3,XA2:3 A2:P2 3"
263
264         # Invalid to valid local partition direct transition tests
265         " C1-3:S+:P2 C2-3:X1:P2 .  .      .      .      .      .     0 A1:1-3,XA1:1-3,A2:2-3:XA2: A1:P2,A2:P-2 1-3"
266         " C1-3:S+:P2 C2-3:X1:P2 .  .      .    X3:P2    .      .     0 A1:1-2,XA1:1-3,A2:3:XA2:3 A1:P2,A2:P2 1-3"
267         "  C0-3:P2   .      .    C4-6   C0-4     .      .      .     0 A1:0-4,B1:4-6 A1:P-2,B1:P0"
268         "  C0-3:P2   .      .    C4-6 C0-4:C0-3  .      .      .     0 A1:0-3,B1:4-6 A1:P2,B1:P0 0-3"
269         "  C0-3:P2   .      .  C3-5:C4-5  .      .      .      .     0 A1:0-3,B1:4-5 A1:P2,B1:P0 0-3"
270
271         # Local partition invalidation tests
272         " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \
273                                    .      .      .      .      .     0 A1:1,A2:2,A3:3 A1:P2,A2:P2,A3:P2 1-3"
274         " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \
275                                    .      .     X4      .      .     0 A1:1-3,A2:1-3,A3:2-3,XA2:,XA3: A1:P2,A2:P-2,A3:P-2 1-3"
276         " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \
277                                    .      .     C4      .      .     0 A1:1-3,A2:1-3,A3:2-3,XA2:,XA3: A1:P2,A2:P-2,A3:P-2 1-3"
278         # Local partition CPU change tests
279         " C0-5:S+:P2 C4-5:S+:P1 .  .      .    C3-5     .      .     0 A1:0-2,A2:3-5 A1:P2,A2:P1 0-2"
280         " C0-5:S+:P2 C4-5:S+:P1 .  .    C1-5     .      .      .     0 A1:1-3,A2:4-5 A1:P2,A2:P1 1-3"
281
282         # cpus_allowed/exclusive_cpus update tests
283         " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \
284                                    .     C4      .      P2     .     0 A1:4,A2:4,XA2:,XA3:,A3:4 \
285                                                                        A1:P0,A3:P-2"
286         " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \
287                                    .     X1      .      P2     .     0 A1:0-3,A2:1-3,XA1:1,XA2:,XA3:,A3:2-3 \
288                                                                        A1:P0,A3:P-2"
289         " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \
290                                    .      .     C3      P2     .     0 A1:0-2,A2:0-2,XA2:3,XA3:3,A3:3 \
291                                                                        A1:P0,A3:P2 3"
292         " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \
293                                    .      .     X3      P2     .     0 A1:0-2,A2:1-2,XA2:3,XA3:3,A3:3 \
294                                                                        A1:P0,A3:P2 3"
295         " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \
296                                    .      .     X3      .      .     0 A1:0-3,A2:1-3,XA2:3,XA3:3,A3:2-3 \
297                                                                        A1:P0,A3:P-2"
298         " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \
299                                    .      .     C3      .      .     0 A1:0-3,A2:3,XA2:3,XA3:3,A3:3 \
300                                                                        A1:P0,A3:P-2"
301         " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \
302                                    .     C4      .      .      .     0 A1:4,A2:4,A3:4,XA1:,XA2:,XA3 \
303                                                                        A1:P0,A3:P-2"
304
305         #  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
306         #  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
307         #
308         # Incorrect change to cpuset.cpus invalidates partition root
309         #
310         # Adding CPUs to partition root that are not in parent's
311         # cpuset.cpus is allowed, but those extra CPUs are ignored.
312         "C2-3:P1:S+ C3:P1   .      .      .     C2-4    .      .     0 A1:,A2:2-3 A1:P1,A2:P1"
313
314         # Taking away all CPUs from parent or itself if there are tasks
315         # will make the partition invalid.
316         "C2-3:P1:S+  C3:P1  .      .      T     C2-3    .      .     0 A1:2-3,A2:2-3 A1:P1,A2:P-1"
317         " C3:P1:S+    C3    .      .      T      P1     .      .     0 A1:3,A2:3 A1:P1,A2:P-1"
318         "$SETUP_A123_PARTITIONS    .    T:C2-3   .      .      .     0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1"
319         "$SETUP_A123_PARTITIONS    . T:C2-3:C1-3 .      .      .     0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
320
321         # Changing a partition root to member makes child partitions invalid
322         "C2-3:P1:S+  C3:P1  .      .      P0     .      .      .     0 A1:2-3,A2:3 A1:P0,A2:P-1"
323         "$SETUP_A123_PARTITIONS    .     C2-3    P0     .      .     0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P0,A3:P-1"
324
325         # cpuset.cpus can contains cpus not in parent's cpuset.cpus as long
326         # as they overlap.
327         "C2-3:P1:S+  .      .      .      .   C3-4:P1   .      .     0 A1:2,A2:3 A1:P1,A2:P1"
328
329         # Deletion of CPUs distributed to child cgroup is allowed.
330         "C0-1:P1:S+ C1      .    C2-3   C4-5     .      .      .     0 A1:4-5,A2:4-5"
331
332         # To become a valid partition root, cpuset.cpus must overlap parent's
333         # cpuset.cpus.
334         "  C0-1:P1   .      .    C2-3    S+   C4-5:P1   .      .     0 A1:0-1,A2:0-1 A1:P1,A2:P-1"
335
336         # Enabling partition with child cpusets is allowed
337         "  C0-1:S+  C1      .    C2-3    P1      .      .      .     0 A1:0-1,A2:1 A1:P1"
338
339         # A partition root with non-partition root parent is invalid, but it
340         # can be made valid if its parent becomes a partition root too.
341         "  C0-1:S+  C1      .    C2-3     .      P2     .      .     0 A1:0-1,A2:1 A1:P0,A2:P-2"
342         "  C0-1:S+ C1:P2    .    C2-3     P1     .      .      .     0 A1:0,A2:1 A1:P1,A2:P2"
343
344         # A non-exclusive cpuset.cpus change will invalidate partition and its siblings
345         "  C0-1:P1   .      .    C2-3   C0-2     .      .      .     0 A1:0-2,B1:2-3 A1:P-1,B1:P0"
346         "  C0-1:P1   .      .  P1:C2-3  C0-2     .      .      .     0 A1:0-2,B1:2-3 A1:P-1,B1:P-1"
347         "   C0-1     .      .  P1:C2-3  C0-2     .      .      .     0 A1:0-2,B1:2-3 A1:P0,B1:P-1"
348
349         #  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
350         #  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
351         # Failure cases:
352
353         # A task cannot be added to a partition with no cpu
354         "C2-3:P1:S+  C3:P1  .      .    O2=0:T   .      .      .     1 A1:,A2:3 A1:P1,A2:P1"
355
356         # Changes to cpuset.cpus.exclusive that violate exclusivity rule is rejected
357         "   C0-3     .      .    C4-5   X0-3     .      .     X3-5   1 A1:0-3,B1:4-5"
358 )
359
360 #
361 # Write to the cpu online file
362 #  $1 - <c>=<v> where <c> = cpu number, <v> value to be written
363 #
364 write_cpu_online()
365 {
366         CPU=${1%=*}
367         VAL=${1#*=}
368         CPUFILE=//sys/devices/system/cpu/cpu${CPU}/online
369         if [[ $VAL -eq 0 ]]
370         then
371                 OFFLINE_CPUS="$OFFLINE_CPUS $CPU"
372         else
373                 [[ -n "$OFFLINE_CPUS" ]] && {
374                         OFFLINE_CPUS=$(echo $CPU $CPU $OFFLINE_CPUS | fmt -1 |\
375                                         sort | uniq -u)
376                 }
377         fi
378         echo $VAL > $CPUFILE
379         pause 0.05
380 }
381
382 #
383 # Set controller state
384 #  $1 - cgroup directory
385 #  $2 - state
386 #  $3 - showerr
387 #
388 # The presence of ":" in state means transition from one to the next.
389 #
390 set_ctrl_state()
391 {
392         TMPMSG=/tmp/.msg_$$
393         CGRP=$1
394         STATE=$2
395         SHOWERR=${3}
396         CTRL=${CTRL:=$CONTROLLER}
397         HASERR=0
398         REDIRECT="2> $TMPMSG"
399         [[ -z "$STATE" || "$STATE" = '.' ]] && return 0
400         [[ $VERBOSE -gt 0 ]] && SHOWERR=1
401
402         rm -f $TMPMSG
403         for CMD in $(echo $STATE | sed -e "s/:/ /g")
404         do
405                 TFILE=$CGRP/cgroup.procs
406                 SFILE=$CGRP/cgroup.subtree_control
407                 PFILE=$CGRP/cpuset.cpus.partition
408                 CFILE=$CGRP/cpuset.cpus
409                 XFILE=$CGRP/cpuset.cpus.exclusive
410                 S=$(expr substr $CMD 1 1)
411                 if [[ $S = S ]]
412                 then
413                         PREFIX=${CMD#?}
414                         COMM="echo ${PREFIX}${CTRL} > $SFILE"
415                         eval $COMM $REDIRECT
416                 elif [[ $S = X ]]
417                 then
418                         CPUS=${CMD#?}
419                         COMM="echo $CPUS > $XFILE"
420                         eval $COMM $REDIRECT
421                 elif [[ $S = C ]]
422                 then
423                         CPUS=${CMD#?}
424                         COMM="echo $CPUS > $CFILE"
425                         eval $COMM $REDIRECT
426                 elif [[ $S = P ]]
427                 then
428                         VAL=${CMD#?}
429                         case $VAL in
430                         0)  VAL=member
431                             ;;
432                         1)  VAL=root
433                             ;;
434                         2)  VAL=isolated
435                             ;;
436                         *)
437                             echo "Invalid partition state - $VAL"
438                             exit 1
439                             ;;
440                         esac
441                         COMM="echo $VAL > $PFILE"
442                         eval $COMM $REDIRECT
443                 elif [[ $S = O ]]
444                 then
445                         VAL=${CMD#?}
446                         write_cpu_online $VAL
447                 elif [[ $S = T ]]
448                 then
449                         COMM="echo 0 > $TFILE"
450                         eval $COMM $REDIRECT
451                 fi
452                 RET=$?
453                 [[ $RET -ne 0 ]] && {
454                         [[ -n "$SHOWERR" ]] && {
455                                 echo "$COMM"
456                                 cat $TMPMSG
457                         }
458                         HASERR=1
459                 }
460                 pause 0.01
461                 rm -f $TMPMSG
462         done
463         return $HASERR
464 }
465
466 set_ctrl_state_noerr()
467 {
468         CGRP=$1
469         STATE=$2
470         [[ -d $CGRP ]] || mkdir $CGRP
471         set_ctrl_state $CGRP $STATE 1
472         [[ $? -ne 0 ]] && {
473                 echo "ERROR: Failed to set $2 to cgroup $1!"
474                 exit 1
475         }
476 }
477
478 online_cpus()
479 {
480         [[ -n "OFFLINE_CPUS" ]] && {
481                 for C in $OFFLINE_CPUS
482                 do
483                         write_cpu_online ${C}=1
484                 done
485         }
486 }
487
488 #
489 # Return 1 if the list of effective cpus isn't the same as the initial list.
490 #
491 reset_cgroup_states()
492 {
493         echo 0 > $CGROUP2/cgroup.procs
494         online_cpus
495         rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1
496         pause 0.02
497         set_ctrl_state . R-
498         pause 0.01
499 }
500
501 dump_states()
502 {
503         for DIR in . A1 A1/A2 A1/A2/A3 B1
504         do
505                 CPUS=$DIR/cpuset.cpus
506                 ECPUS=$DIR/cpuset.cpus.effective
507                 XCPUS=$DIR/cpuset.cpus.exclusive
508                 XECPUS=$DIR/cpuset.cpus.exclusive.effective
509                 PRS=$DIR/cpuset.cpus.partition
510                 PCPUS=$DIR/.__DEBUG__.cpuset.cpus.subpartitions
511                 ISCPUS=$DIR/cpuset.cpus.isolated
512                 [[ -e $CPUS   ]] && echo "$CPUS: $(cat $CPUS)"
513                 [[ -e $XCPUS  ]] && echo "$XCPUS: $(cat $XCPUS)"
514                 [[ -e $ECPUS  ]] && echo "$ECPUS: $(cat $ECPUS)"
515                 [[ -e $XECPUS ]] && echo "$XECPUS: $(cat $XECPUS)"
516                 [[ -e $PRS    ]] && echo "$PRS: $(cat $PRS)"
517                 [[ -e $PCPUS  ]] && echo "$PCPUS: $(cat $PCPUS)"
518                 [[ -e $ISCPUS ]] && echo "$ISCPUS: $(cat $ISCPUS)"
519         done
520 }
521
522 #
523 # Check effective cpus
524 # $1 - check string, format: <cgroup>:<cpu-list>[,<cgroup>:<cpu-list>]*
525 #
526 check_effective_cpus()
527 {
528         CHK_STR=$1
529         for CHK in $(echo $CHK_STR | sed -e "s/,/ /g")
530         do
531                 set -- $(echo $CHK | sed -e "s/:/ /g")
532                 CGRP=$1
533                 CPUS=$2
534                 if [[ $CGRP = X* ]]
535                 then
536                         CGRP=${CGRP#X}
537                         FILE=cpuset.cpus.exclusive.effective
538                 else
539                         FILE=cpuset.cpus.effective
540                 fi
541                 [[ $CGRP = A2 ]] && CGRP=A1/A2
542                 [[ $CGRP = A3 ]] && CGRP=A1/A2/A3
543                 [[ -e $CGRP/$FILE ]] || return 1
544                 [[ $CPUS = $(cat $CGRP/$FILE) ]] || return 1
545         done
546 }
547
548 #
549 # Check cgroup states
550 #  $1 - check string, format: <cgroup>:<state>[,<cgroup>:<state>]*
551 #
552 check_cgroup_states()
553 {
554         CHK_STR=$1
555         for CHK in $(echo $CHK_STR | sed -e "s/,/ /g")
556         do
557                 set -- $(echo $CHK | sed -e "s/:/ /g")
558                 CGRP=$1
559                 STATE=$2
560                 FILE=
561                 EVAL=$(expr substr $STATE 2 2)
562                 [[ $CGRP = A2 ]] && CGRP=A1/A2
563                 [[ $CGRP = A3 ]] && CGRP=A1/A2/A3
564
565                 case $STATE in
566                         P*) FILE=$CGRP/cpuset.cpus.partition
567                             ;;
568                         *)  echo "Unknown state: $STATE!"
569                             exit 1
570                             ;;
571                 esac
572                 VAL=$(cat $FILE)
573
574                 case "$VAL" in
575                         member) VAL=0
576                                 ;;
577                         root)   VAL=1
578                                 ;;
579                         isolated)
580                                 VAL=2
581                                 ;;
582                         "root invalid"*)
583                                 VAL=-1
584                                 ;;
585                         "isolated invalid"*)
586                                 VAL=-2
587                                 ;;
588                 esac
589                 [[ $EVAL != $VAL ]] && return 1
590         done
591         return 0
592 }
593
594 #
595 # Get isolated (including offline) CPUs by looking at
596 # /sys/kernel/debug/sched/domains and cpuset.cpus.isolated control file,
597 # if available, and compare that with the expected value.
598 #
599 # Note that isolated CPUs from the sched/domains context include offline
600 # CPUs as well as CPUs in non-isolated 1-CPU partition. Those CPUs may
601 # not be included in the cpuset.cpus.isolated control file which contains
602 # only CPUs in isolated partitions.
603 #
604 # $1 - expected isolated cpu list(s) <isolcpus1>{,<isolcpus2>}
605 # <isolcpus1> - expected sched/domains value
606 # <isolcpus2> - cpuset.cpus.isolated value = <isolcpus1> if not defined
607 #
608 check_isolcpus()
609 {
610         EXPECT_VAL=$1
611         ISOLCPUS=
612         LASTISOLCPU=
613         SCHED_DOMAINS=/sys/kernel/debug/sched/domains
614         ISCPUS=${CGROUP2}/cpuset.cpus.isolated
615         if [[ $EXPECT_VAL = . ]]
616         then
617                 EXPECT_VAL=
618                 EXPECT_VAL2=
619         elif [[ $(expr $EXPECT_VAL : ".*,.*") > 0 ]]
620         then
621                 set -- $(echo $EXPECT_VAL | sed -e "s/,/ /g")
622                 EXPECT_VAL=$1
623                 EXPECT_VAL2=$2
624         else
625                 EXPECT_VAL2=$EXPECT_VAL
626         fi
627
628         #
629         # Check the debug isolated cpumask, if present
630         #
631         [[ -f $ISCPUS ]] && {
632                 ISOLCPUS=$(cat $ISCPUS)
633                 [[ "$EXPECT_VAL2" != "$ISOLCPUS" ]] && {
634                         # Take a 50ms pause and try again
635                         pause 0.05
636                         ISOLCPUS=$(cat $ISCPUS)
637                 }
638                 [[ "$EXPECT_VAL2" != "$ISOLCPUS" ]] && return 1
639                 ISOLCPUS=
640         }
641
642         #
643         # Use the sched domain in debugfs to check isolated CPUs, if available
644         #
645         [[ -d $SCHED_DOMAINS ]] || return 0
646
647         for ((CPU=0; CPU < $NR_CPUS; CPU++))
648         do
649                 [[ -n "$(ls ${SCHED_DOMAINS}/cpu$CPU)" ]] && continue
650
651                 if [[ -z "$LASTISOLCPU" ]]
652                 then
653                         ISOLCPUS=$CPU
654                         LASTISOLCPU=$CPU
655                 elif [[ "$LASTISOLCPU" -eq $((CPU - 1)) ]]
656                 then
657                         echo $ISOLCPUS | grep -q "\<$LASTISOLCPU\$"
658                         if [[ $? -eq 0 ]]
659                         then
660                                 ISOLCPUS=${ISOLCPUS}-
661                         fi
662                         LASTISOLCPU=$CPU
663                 else
664                         if [[ $ISOLCPUS = *- ]]
665                         then
666                                 ISOLCPUS=${ISOLCPUS}$LASTISOLCPU
667                         fi
668                         ISOLCPUS=${ISOLCPUS},$CPU
669                         LASTISOLCPU=$CPU
670                 fi
671         done
672         [[ "$ISOLCPUS" = *- ]] && ISOLCPUS=${ISOLCPUS}$LASTISOLCPU
673         [[ "$EXPECT_VAL" = "$ISOLCPUS" ]]
674 }
675
676 test_fail()
677 {
678         TESTNUM=$1
679         TESTTYPE=$2
680         ADDINFO=$3
681         echo "Test $TEST[$TESTNUM] failed $TESTTYPE check!"
682         [[ -n "$ADDINFO" ]] && echo "*** $ADDINFO ***"
683         eval echo \${$TEST[$I]}
684         echo
685         dump_states
686         exit 1
687 }
688
689 #
690 # Check to see if there are unexpected isolated CPUs left
691 #
692 null_isolcpus_check()
693 {
694         [[ $VERBOSE -gt 0 ]] || return 0
695         # Retry a few times before printing error
696         RETRY=0
697         while [[ $RETRY -lt 5 ]]
698         do
699                 pause 0.01
700                 check_isolcpus "."
701                 [[ $? -eq 0 ]] && return 0
702                 ((RETRY++))
703         done
704         echo "Unexpected isolated CPUs: $ISOLCPUS"
705         dump_states
706         exit 1
707 }
708
709 #
710 # Run cpuset state transition test
711 #  $1 - test matrix name
712 #
713 # This test is somewhat fragile as delays (sleep x) are added in various
714 # places to make sure state changes are fully propagated before the next
715 # action. These delays may need to be adjusted if running in a slower machine.
716 #
717 run_state_test()
718 {
719         TEST=$1
720         CONTROLLER=cpuset
721         I=0
722         eval CNT="\${#$TEST[@]}"
723
724         reset_cgroup_states
725         console_msg "Running state transition test ..."
726
727         while [[ $I -lt $CNT ]]
728         do
729                 echo "Running test $I ..." > /dev/console
730                 [[ $VERBOSE -gt 1 ]] && {
731                         echo ""
732                         eval echo \${$TEST[$I]}
733                 }
734                 eval set -- "\${$TEST[$I]}"
735                 OLD_A1=$1
736                 OLD_A2=$2
737                 OLD_A3=$3
738                 OLD_B1=$4
739                 NEW_A1=$5
740                 NEW_A2=$6
741                 NEW_A3=$7
742                 NEW_B1=$8
743                 RESULT=$9
744                 ECPUS=${10}
745                 STATES=${11}
746                 ICPUS=${12}
747
748                 set_ctrl_state_noerr B1       $OLD_B1
749                 set_ctrl_state_noerr A1       $OLD_A1
750                 set_ctrl_state_noerr A1/A2    $OLD_A2
751                 set_ctrl_state_noerr A1/A2/A3 $OLD_A3
752                 RETVAL=0
753                 set_ctrl_state A1       $NEW_A1; ((RETVAL += $?))
754                 set_ctrl_state A1/A2    $NEW_A2; ((RETVAL += $?))
755                 set_ctrl_state A1/A2/A3 $NEW_A3; ((RETVAL += $?))
756                 set_ctrl_state B1       $NEW_B1; ((RETVAL += $?))
757
758                 [[ $RETVAL -ne $RESULT ]] && test_fail $I result
759
760                 [[ -n "$ECPUS" && "$ECPUS" != . ]] && {
761                         check_effective_cpus $ECPUS
762                         [[ $? -ne 0 ]] && test_fail $I "effective CPU"
763                 }
764
765                 [[ -n "$STATES" && "$STATES" != . ]] && {
766                         check_cgroup_states $STATES
767                         [[ $? -ne 0 ]] && test_fail $I states
768                 }
769
770                 # Compare the expected isolated CPUs with the actual ones,
771                 # if available
772                 [[ -n "$ICPUS" ]] && {
773                         check_isolcpus $ICPUS
774                         [[ $? -ne 0 ]] && test_fail $I "isolated CPU" \
775                                 "Expect $ICPUS, get $ISOLCPUS instead"
776                 }
777                 reset_cgroup_states
778                 #
779                 # Check to see if effective cpu list changes
780                 #
781                 NEWLIST=$(cat cpuset.cpus.effective)
782                 RETRY=0
783                 while [[ $NEWLIST != $CPULIST && $RETRY -lt 8 ]]
784                 do
785                         # Wait a bit longer & recheck a few times
786                         pause 0.01
787                         ((RETRY++))
788                         NEWLIST=$(cat cpuset.cpus.effective)
789                 done
790                 [[ $NEWLIST != $CPULIST ]] && {
791                         echo "Effective cpus changed to $NEWLIST after test $I!"
792                         exit 1
793                 }
794                 null_isolcpus_check
795                 [[ $VERBOSE -gt 0 ]] && echo "Test $I done."
796                 ((I++))
797         done
798         echo "All $I tests of $TEST PASSED."
799 }
800
801 #
802 # Testing the new "isolated" partition root type
803 #
804 test_isolated()
805 {
806         cd $CGROUP2/test
807         echo 2-3 > cpuset.cpus
808         TYPE=$(cat cpuset.cpus.partition)
809         [[ $TYPE = member ]] || echo member > cpuset.cpus.partition
810
811         console_msg "Change from member to root"
812         test_partition root
813
814         console_msg "Change from root to isolated"
815         test_partition isolated
816
817         console_msg "Change from isolated to member"
818         test_partition member
819
820         console_msg "Change from member to isolated"
821         test_partition isolated
822
823         console_msg "Change from isolated to root"
824         test_partition root
825
826         console_msg "Change from root to member"
827         test_partition member
828
829         #
830         # Testing partition root with no cpu
831         #
832         console_msg "Distribute all cpus to child partition"
833         echo +cpuset > cgroup.subtree_control
834         test_partition root
835
836         mkdir A1
837         cd A1
838         echo 2-3 > cpuset.cpus
839         test_partition root
840         test_effective_cpus 2-3
841         cd ..
842         test_effective_cpus ""
843
844         console_msg "Moving task to partition test"
845         test_add_proc "No space left"
846         cd A1
847         test_add_proc ""
848         cd ..
849
850         console_msg "Shrink and expand child partition"
851         cd A1
852         echo 2 > cpuset.cpus
853         cd ..
854         test_effective_cpus 3
855         cd A1
856         echo 2-3 > cpuset.cpus
857         cd ..
858         test_effective_cpus ""
859
860         # Cleaning up
861         console_msg "Cleaning up"
862         echo $$ > $CGROUP2/cgroup.procs
863         [[ -d A1 ]] && rmdir A1
864         null_isolcpus_check
865 }
866
867 #
868 # Wait for inotify event for the given file and read it
869 # $1: cgroup file to wait for
870 # $2: file to store the read result
871 #
872 wait_inotify()
873 {
874         CGROUP_FILE=$1
875         OUTPUT_FILE=$2
876
877         $WAIT_INOTIFY $CGROUP_FILE
878         cat $CGROUP_FILE > $OUTPUT_FILE
879 }
880
881 #
882 # Test if inotify events are properly generated when going into and out of
883 # invalid partition state.
884 #
885 test_inotify()
886 {
887         ERR=0
888         PRS=/tmp/.prs_$$
889         cd $CGROUP2/test
890         [[ -f $WAIT_INOTIFY ]] || {
891                 echo "wait_inotify not found, inotify test SKIPPED."
892                 return
893         }
894
895         pause 0.01
896         echo 1 > cpuset.cpus
897         echo 0 > cgroup.procs
898         echo root > cpuset.cpus.partition
899         pause 0.01
900         rm -f $PRS
901         wait_inotify $PWD/cpuset.cpus.partition $PRS &
902         pause 0.01
903         set_ctrl_state . "O1=0"
904         pause 0.01
905         check_cgroup_states ".:P-1"
906         if [[ $? -ne 0 ]]
907         then
908                 echo "FAILED: Inotify test - partition not invalid"
909                 ERR=1
910         elif [[ ! -f $PRS ]]
911         then
912                 echo "FAILED: Inotify test - event not generated"
913                 ERR=1
914                 kill %1
915         elif [[ $(cat $PRS) != "root invalid"* ]]
916         then
917                 echo "FAILED: Inotify test - incorrect state"
918                 cat $PRS
919                 ERR=1
920         fi
921         online_cpus
922         echo member > cpuset.cpus.partition
923         echo 0 > ../cgroup.procs
924         if [[ $ERR -ne 0 ]]
925         then
926                 exit 1
927         else
928                 echo "Inotify test PASSED"
929         fi
930 }
931
932 trap cleanup 0 2 3 6
933 run_state_test TEST_MATRIX
934 test_isolated
935 test_inotify
936 echo "All tests PASSED."