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