Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# Kselftest framework requirement - SKIP code is 4.
5ksft_skip=4
6
7set -e
8
9if [[ $(id -u) -ne 0 ]]; then
10 echo "This test must be run as root. Skipping..."
11 exit $ksft_skip
12fi
13
14if ! command -v killall >/dev/null 2>&1; then
15 echo "killall not available. Skipping..."
16 exit $ksft_skip
17fi
18
19nr_hugepgs=$(cat /proc/sys/vm/nr_hugepages)
20
21fault_limit_file=limit_in_bytes
22reservation_limit_file=rsvd.limit_in_bytes
23fault_usage_file=usage_in_bytes
24reservation_usage_file=rsvd.usage_in_bytes
25
26if [[ "$1" == "-cgroup-v2" ]]; then
27 cgroup2=1
28 fault_limit_file=max
29 reservation_limit_file=rsvd.max
30 fault_usage_file=current
31 reservation_usage_file=rsvd.current
32fi
33
34if [[ $cgroup2 ]]; then
35 cgroup_path=$(mount -t cgroup2 | head -1 | awk '{print $3}')
36 if [[ -z "$cgroup_path" ]]; then
37 cgroup_path=$(mktemp -d)
38 mount -t cgroup2 none $cgroup_path
39 do_umount=1
40 fi
41 echo "+hugetlb" >$cgroup_path/cgroup.subtree_control
42else
43 cgroup_path=$(mount -t cgroup | grep ",hugetlb" | awk '{print $3}')
44 if [[ -z "$cgroup_path" ]]; then
45 cgroup_path=$(mktemp -d)
46 mount -t cgroup memory,hugetlb $cgroup_path
47 do_umount=1
48 fi
49fi
50export cgroup_path
51
52function cleanup() {
53 if [[ $cgroup2 ]]; then
54 echo $$ >$cgroup_path/cgroup.procs
55 else
56 echo $$ >$cgroup_path/tasks
57 fi
58
59 if [[ -e /mnt/huge ]]; then
60 rm -rf /mnt/huge/*
61 umount /mnt/huge || echo error
62 rmdir /mnt/huge
63 fi
64 if [[ -e $cgroup_path/hugetlb_cgroup_test ]]; then
65 rmdir $cgroup_path/hugetlb_cgroup_test
66 fi
67 if [[ -e $cgroup_path/hugetlb_cgroup_test1 ]]; then
68 rmdir $cgroup_path/hugetlb_cgroup_test1
69 fi
70 if [[ -e $cgroup_path/hugetlb_cgroup_test2 ]]; then
71 rmdir $cgroup_path/hugetlb_cgroup_test2
72 fi
73 echo 0 >/proc/sys/vm/nr_hugepages
74 echo CLEANUP DONE
75}
76
77function expect_equal() {
78 local expected="$1"
79 local actual="$2"
80 local error="$3"
81
82 if [[ "$expected" != "$actual" ]]; then
83 echo "expected ($expected) != actual ($actual): $3"
84 cleanup
85 exit 1
86 fi
87}
88
89function get_machine_hugepage_size() {
90 hpz=$(grep -i hugepagesize /proc/meminfo)
91 kb=${hpz:14:-3}
92 mb=$(($kb / 1024))
93 echo $mb
94}
95
96MB=$(get_machine_hugepage_size)
97
98function setup_cgroup() {
99 local name="$1"
100 local cgroup_limit="$2"
101 local reservation_limit="$3"
102
103 mkdir $cgroup_path/$name
104
105 echo writing cgroup limit: "$cgroup_limit"
106 echo "$cgroup_limit" >$cgroup_path/$name/hugetlb.${MB}MB.$fault_limit_file
107
108 echo writing reservation limit: "$reservation_limit"
109 echo "$reservation_limit" > \
110 $cgroup_path/$name/hugetlb.${MB}MB.$reservation_limit_file
111
112 if [ -e "$cgroup_path/$name/cpuset.cpus" ]; then
113 echo 0 >$cgroup_path/$name/cpuset.cpus
114 fi
115 if [ -e "$cgroup_path/$name/cpuset.mems" ]; then
116 echo 0 >$cgroup_path/$name/cpuset.mems
117 fi
118}
119
120function wait_for_file_value() {
121 local path="$1"
122 local expect="$2"
123 local max_tries=60
124
125 if [[ ! -r "$path" ]]; then
126 echo "ERROR: cannot read '$path', missing or permission denied"
127 return 1
128 fi
129
130 for ((i=1; i<=max_tries; i++)); do
131 local cur="$(cat "$path")"
132 if [[ "$cur" == "$expect" ]]; then
133 return 0
134 fi
135 echo "Waiting for $path to become '$expect' (current: '$cur') (try $i/$max_tries)"
136 sleep 1
137 done
138
139 echo "ERROR: timeout waiting for $path to become '$expect'"
140 return 1
141}
142
143function wait_for_hugetlb_memory_to_get_depleted() {
144 local cgroup="$1"
145 local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file"
146
147 wait_for_file_value "$path" "0"
148}
149
150function wait_for_hugetlb_memory_to_get_reserved() {
151 local cgroup="$1"
152 local size="$2"
153 local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file"
154
155 wait_for_file_value "$path" "$size"
156}
157
158function wait_for_hugetlb_memory_to_get_written() {
159 local cgroup="$1"
160 local size="$2"
161 local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$fault_usage_file"
162
163 wait_for_file_value "$path" "$size"
164}
165
166function write_hugetlbfs_and_get_usage() {
167 local cgroup="$1"
168 local size="$2"
169 local populate="$3"
170 local write="$4"
171 local path="$5"
172 local method="$6"
173 local private="$7"
174 local expect_failure="$8"
175 local reserve="$9"
176
177 # Function return values.
178 reservation_failed=0
179 oom_killed=0
180 hugetlb_difference=0
181 reserved_difference=0
182
183 local hugetlb_usage=$cgroup_path/$cgroup/hugetlb.${MB}MB.$fault_usage_file
184 local reserved_usage=$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file
185
186 local hugetlb_before=$(cat $hugetlb_usage)
187 local reserved_before=$(cat $reserved_usage)
188
189 echo
190 echo Starting:
191 echo hugetlb_usage="$hugetlb_before"
192 echo reserved_usage="$reserved_before"
193 echo expect_failure is "$expect_failure"
194
195 output=$(mktemp)
196 set +e
197 if [[ "$method" == "1" ]] || [[ "$method" == 2 ]] ||
198 [[ "$private" == "-r" ]] && [[ "$expect_failure" != 1 ]]; then
199
200 bash write_hugetlb_memory.sh "$size" "$populate" "$write" \
201 "$cgroup" "$path" "$method" "$private" "-l" "$reserve" 2>&1 | tee $output &
202
203 local write_result=$?
204 local write_pid=$!
205
206 until grep -q -i "DONE" $output; do
207 echo waiting for DONE signal.
208 if ! ps $write_pid > /dev/null
209 then
210 echo "FAIL: The write died"
211 cleanup
212 exit 1
213 fi
214 sleep 0.5
215 done
216
217 echo ================= write_hugetlb_memory.sh output is:
218 cat $output
219 echo ================= end output.
220
221 if [[ "$populate" == "-o" ]] || [[ "$write" == "-w" ]]; then
222 wait_for_hugetlb_memory_to_get_written "$cgroup" "$size"
223 elif [[ "$reserve" != "-n" ]]; then
224 wait_for_hugetlb_memory_to_get_reserved "$cgroup" "$size"
225 else
226 # This case doesn't produce visible effects, but we still have
227 # to wait for the async process to start and execute...
228 sleep 0.5
229 fi
230
231 echo write_result is $write_result
232 else
233 bash write_hugetlb_memory.sh "$size" "$populate" "$write" \
234 "$cgroup" "$path" "$method" "$private" "$reserve"
235 local write_result=$?
236
237 if [[ "$reserve" != "-n" ]]; then
238 wait_for_hugetlb_memory_to_get_reserved "$cgroup" "$size"
239 fi
240 fi
241 set -e
242
243 if [[ "$write_result" == 1 ]]; then
244 reservation_failed=1
245 fi
246
247 # On linus/master, the above process gets SIGBUS'd on oomkill, with
248 # return code 135. On earlier kernels, it gets actual oomkill, with return
249 # code 137, so just check for both conditions in case we're testing
250 # against an earlier kernel.
251 if [[ "$write_result" == 135 ]] || [[ "$write_result" == 137 ]]; then
252 oom_killed=1
253 fi
254
255 local hugetlb_after=$(cat $hugetlb_usage)
256 local reserved_after=$(cat $reserved_usage)
257
258 echo After write:
259 echo hugetlb_usage="$hugetlb_after"
260 echo reserved_usage="$reserved_after"
261
262 hugetlb_difference=$(($hugetlb_after - $hugetlb_before))
263 reserved_difference=$(($reserved_after - $reserved_before))
264}
265
266function cleanup_hugetlb_memory() {
267 set +e
268 local cgroup="$1"
269 if [[ "$(pgrep -f write_to_hugetlbfs)" != "" ]]; then
270 echo killing write_to_hugetlbfs
271 killall -2 --wait write_to_hugetlbfs
272 wait_for_hugetlb_memory_to_get_depleted $cgroup
273 fi
274 set -e
275
276 if [[ -e /mnt/huge ]]; then
277 rm -rf /mnt/huge/*
278 umount /mnt/huge
279 rmdir /mnt/huge
280 fi
281}
282
283function run_test() {
284 local size=$(($1 * ${MB} * 1024 * 1024))
285 local populate="$2"
286 local write="$3"
287 local cgroup_limit=$(($4 * ${MB} * 1024 * 1024))
288 local reservation_limit=$(($5 * ${MB} * 1024 * 1024))
289 local nr_hugepages="$6"
290 local method="$7"
291 local private="$8"
292 local expect_failure="$9"
293 local reserve="${10}"
294
295 # Function return values.
296 hugetlb_difference=0
297 reserved_difference=0
298 reservation_failed=0
299 oom_killed=0
300
301 echo nr hugepages = "$nr_hugepages"
302 echo "$nr_hugepages" >/proc/sys/vm/nr_hugepages
303
304 setup_cgroup "hugetlb_cgroup_test" "$cgroup_limit" "$reservation_limit"
305
306 mkdir -p /mnt/huge
307 mount -t hugetlbfs -o pagesize=${MB}M none /mnt/huge
308
309 write_hugetlbfs_and_get_usage "hugetlb_cgroup_test" "$size" "$populate" \
310 "$write" "/mnt/huge/test" "$method" "$private" "$expect_failure" \
311 "$reserve"
312
313 cleanup_hugetlb_memory "hugetlb_cgroup_test"
314
315 local final_hugetlb=$(cat $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB}MB.$fault_usage_file)
316 local final_reservation=$(cat $cgroup_path/hugetlb_cgroup_test/hugetlb.${MB}MB.$reservation_usage_file)
317
318 echo $hugetlb_difference
319 echo $reserved_difference
320 expect_equal "0" "$final_hugetlb" "final hugetlb is not zero"
321 expect_equal "0" "$final_reservation" "final reservation is not zero"
322}
323
324function run_multiple_cgroup_test() {
325 local size1="$1"
326 local populate1="$2"
327 local write1="$3"
328 local cgroup_limit1="$4"
329 local reservation_limit1="$5"
330
331 local size2="$6"
332 local populate2="$7"
333 local write2="$8"
334 local cgroup_limit2="$9"
335 local reservation_limit2="${10}"
336
337 local nr_hugepages="${11}"
338 local method="${12}"
339 local private="${13}"
340 local expect_failure="${14}"
341 local reserve="${15}"
342
343 # Function return values.
344 hugetlb_difference1=0
345 reserved_difference1=0
346 reservation_failed1=0
347 oom_killed1=0
348
349 hugetlb_difference2=0
350 reserved_difference2=0
351 reservation_failed2=0
352 oom_killed2=0
353
354 echo nr hugepages = "$nr_hugepages"
355 echo "$nr_hugepages" >/proc/sys/vm/nr_hugepages
356
357 setup_cgroup "hugetlb_cgroup_test1" "$cgroup_limit1" "$reservation_limit1"
358 setup_cgroup "hugetlb_cgroup_test2" "$cgroup_limit2" "$reservation_limit2"
359
360 mkdir -p /mnt/huge
361 mount -t hugetlbfs -o pagesize=${MB}M none /mnt/huge
362
363 write_hugetlbfs_and_get_usage "hugetlb_cgroup_test1" "$size1" \
364 "$populate1" "$write1" "/mnt/huge/test1" "$method" "$private" \
365 "$expect_failure" "$reserve"
366
367 hugetlb_difference1=$hugetlb_difference
368 reserved_difference1=$reserved_difference
369 reservation_failed1=$reservation_failed
370 oom_killed1=$oom_killed
371
372 local cgroup1_hugetlb_usage=$cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB}MB.$fault_usage_file
373 local cgroup1_reservation_usage=$cgroup_path/hugetlb_cgroup_test1/hugetlb.${MB}MB.$reservation_usage_file
374 local cgroup2_hugetlb_usage=$cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB}MB.$fault_usage_file
375 local cgroup2_reservation_usage=$cgroup_path/hugetlb_cgroup_test2/hugetlb.${MB}MB.$reservation_usage_file
376
377 local usage_before_second_write=$(cat $cgroup1_hugetlb_usage)
378 local reservation_usage_before_second_write=$(cat $cgroup1_reservation_usage)
379
380 write_hugetlbfs_and_get_usage "hugetlb_cgroup_test2" "$size2" \
381 "$populate2" "$write2" "/mnt/huge/test2" "$method" "$private" \
382 "$expect_failure" "$reserve"
383
384 hugetlb_difference2=$hugetlb_difference
385 reserved_difference2=$reserved_difference
386 reservation_failed2=$reservation_failed
387 oom_killed2=$oom_killed
388
389 expect_equal "$usage_before_second_write" \
390 "$(cat $cgroup1_hugetlb_usage)" "Usage changed."
391 expect_equal "$reservation_usage_before_second_write" \
392 "$(cat $cgroup1_reservation_usage)" "Reservation usage changed."
393
394 cleanup_hugetlb_memory
395
396 local final_hugetlb=$(cat $cgroup1_hugetlb_usage)
397 local final_reservation=$(cat $cgroup1_reservation_usage)
398
399 expect_equal "0" "$final_hugetlb" \
400 "hugetlbt_cgroup_test1 final hugetlb is not zero"
401 expect_equal "0" "$final_reservation" \
402 "hugetlbt_cgroup_test1 final reservation is not zero"
403
404 local final_hugetlb=$(cat $cgroup2_hugetlb_usage)
405 local final_reservation=$(cat $cgroup2_reservation_usage)
406
407 expect_equal "0" "$final_hugetlb" \
408 "hugetlb_cgroup_test2 final hugetlb is not zero"
409 expect_equal "0" "$final_reservation" \
410 "hugetlb_cgroup_test2 final reservation is not zero"
411}
412
413cleanup
414
415for populate in "" "-o"; do
416 for method in 0 1 2; do
417 for private in "" "-r"; do
418 for reserve in "" "-n"; do
419
420 # Skip mmap(MAP_HUGETLB | MAP_SHARED). Doesn't seem to be supported.
421 if [[ "$method" == 1 ]] && [[ "$private" == "" ]]; then
422 continue
423 fi
424
425 # Skip populated shmem tests. Doesn't seem to be supported.
426 if [[ "$method" == 2"" ]] && [[ "$populate" == "-o" ]]; then
427 continue
428 fi
429
430 if [[ "$method" == 2"" ]] && [[ "$reserve" == "-n" ]]; then
431 continue
432 fi
433
434 cleanup
435 echo
436 echo
437 echo
438 echo Test normal case.
439 echo private=$private, populate=$populate, method=$method, reserve=$reserve
440 run_test 5 "$populate" "" 10 10 10 "$method" "$private" "0" "$reserve"
441
442 echo Memory charged to hugtlb=$hugetlb_difference
443 echo Memory charged to reservation=$reserved_difference
444
445 if [[ "$populate" == "-o" ]]; then
446 expect_equal "$((5 * $MB * 1024 * 1024))" "$hugetlb_difference" \
447 "Reserved memory charged to hugetlb cgroup."
448 else
449 expect_equal "0" "$hugetlb_difference" \
450 "Reserved memory charged to hugetlb cgroup."
451 fi
452
453 if [[ "$reserve" != "-n" ]] || [[ "$populate" == "-o" ]]; then
454 expect_equal "$((5 * $MB * 1024 * 1024))" "$reserved_difference" \
455 "Reserved memory not charged to reservation usage."
456 else
457 expect_equal "0" "$reserved_difference" \
458 "Reserved memory not charged to reservation usage."
459 fi
460
461 echo 'PASS'
462
463 cleanup
464 echo
465 echo
466 echo
467 echo Test normal case with write.
468 echo private=$private, populate=$populate, method=$method, reserve=$reserve
469 run_test 5 "$populate" '-w' 5 5 10 "$method" "$private" "0" "$reserve"
470
471 echo Memory charged to hugtlb=$hugetlb_difference
472 echo Memory charged to reservation=$reserved_difference
473
474 expect_equal "$((5 * $MB * 1024 * 1024))" "$hugetlb_difference" \
475 "Reserved memory charged to hugetlb cgroup."
476
477 expect_equal "$((5 * $MB * 1024 * 1024))" "$reserved_difference" \
478 "Reserved memory not charged to reservation usage."
479
480 echo 'PASS'
481
482 cleanup
483 continue
484 echo
485 echo
486 echo
487 echo Test more than reservation case.
488 echo private=$private, populate=$populate, method=$method, reserve=$reserve
489
490 if [ "$reserve" != "-n" ]; then
491 run_test "5" "$populate" '' "10" "2" "10" "$method" "$private" "1" \
492 "$reserve"
493
494 expect_equal "1" "$reservation_failed" "Reservation succeeded."
495 fi
496
497 echo 'PASS'
498
499 cleanup
500
501 echo
502 echo
503 echo
504 echo Test more than cgroup limit case.
505 echo private=$private, populate=$populate, method=$method, reserve=$reserve
506
507 # Not sure if shm memory can be cleaned up when the process gets sigbus'd.
508 if [[ "$method" != 2 ]]; then
509 run_test 5 "$populate" "-w" 2 10 10 "$method" "$private" "1" "$reserve"
510
511 expect_equal "1" "$oom_killed" "Not oom killed."
512 fi
513 echo 'PASS'
514
515 cleanup
516
517 echo
518 echo
519 echo
520 echo Test normal case, multiple cgroups.
521 echo private=$private, populate=$populate, method=$method, reserve=$reserve
522 run_multiple_cgroup_test "3" "$populate" "" "10" "10" "5" \
523 "$populate" "" "10" "10" "10" \
524 "$method" "$private" "0" "$reserve"
525
526 echo Memory charged to hugtlb1=$hugetlb_difference1
527 echo Memory charged to reservation1=$reserved_difference1
528 echo Memory charged to hugtlb2=$hugetlb_difference2
529 echo Memory charged to reservation2=$reserved_difference2
530
531 if [[ "$reserve" != "-n" ]] || [[ "$populate" == "-o" ]]; then
532 expect_equal "3" "$reserved_difference1" \
533 "Incorrect reservations charged to cgroup 1."
534
535 expect_equal "5" "$reserved_difference2" \
536 "Incorrect reservation charged to cgroup 2."
537
538 else
539 expect_equal "0" "$reserved_difference1" \
540 "Incorrect reservations charged to cgroup 1."
541
542 expect_equal "0" "$reserved_difference2" \
543 "Incorrect reservation charged to cgroup 2."
544 fi
545
546 if [[ "$populate" == "-o" ]]; then
547 expect_equal "3" "$hugetlb_difference1" \
548 "Incorrect hugetlb charged to cgroup 1."
549
550 expect_equal "5" "$hugetlb_difference2" \
551 "Incorrect hugetlb charged to cgroup 2."
552
553 else
554 expect_equal "0" "$hugetlb_difference1" \
555 "Incorrect hugetlb charged to cgroup 1."
556
557 expect_equal "0" "$hugetlb_difference2" \
558 "Incorrect hugetlb charged to cgroup 2."
559 fi
560 echo 'PASS'
561
562 cleanup
563 echo
564 echo
565 echo
566 echo Test normal case with write, multiple cgroups.
567 echo private=$private, populate=$populate, method=$method, reserve=$reserve
568 run_multiple_cgroup_test "3" "$populate" "-w" "10" "10" "5" \
569 "$populate" "-w" "10" "10" "10" \
570 "$method" "$private" "0" "$reserve"
571
572 echo Memory charged to hugtlb1=$hugetlb_difference1
573 echo Memory charged to reservation1=$reserved_difference1
574 echo Memory charged to hugtlb2=$hugetlb_difference2
575 echo Memory charged to reservation2=$reserved_difference2
576
577 expect_equal "3" "$hugetlb_difference1" \
578 "Incorrect hugetlb charged to cgroup 1."
579
580 expect_equal "3" "$reserved_difference1" \
581 "Incorrect reservation charged to cgroup 1."
582
583 expect_equal "5" "$hugetlb_difference2" \
584 "Incorrect hugetlb charged to cgroup 2."
585
586 expect_equal "5" "$reserved_difference2" \
587 "Incorrected reservation charged to cgroup 2."
588 echo 'PASS'
589
590 cleanup
591
592 done # reserve
593 done # private
594 done # populate
595done # method
596
597if [[ $do_umount ]]; then
598 umount $cgroup_path
599 rmdir $cgroup_path
600fi
601
602echo "$nr_hugepgs" > /proc/sys/vm/nr_hugepages