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