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