• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/sh
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
24cgroup_path=/dev/cgroup/memory
25if [[ ! -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
32fi
33
34if [[ $cgroup2 ]]; then
35  echo "+hugetlb" >/dev/cgroup/memory/cgroup.subtree_control
36fi
37
38function 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
63function 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
75function 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
82MB=$(get_machine_hugepage_size)
83
84function 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
106function 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
117function 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
130function 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
143function 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
243function 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
260function 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
301function 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
390cleanup
391
392for 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
572done       # method
573
574umount $cgroup_path
575rmdir $cgroup_path
576