Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hugetlb_cgroup: add hugetlb_cgroup reservation tests

The tests use both shared and private mapped hugetlb memory, and monitors
the hugetlb usage counter as well as the hugetlb reservation counter.
They test different configurations such as hugetlb memory usage via
hugetlbfs, or MAP_HUGETLB, or shmget/shmat, and with and without
MAP_POPULATE.

Also add test for hugetlb reservation reparenting, since this is a subtle
issue.

Signed-off-by: Mina Almasry <almasrymina@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Tested-by: Sandipan Das <sandipan@linux.ibm.com> [powerpc64]
Acked-by: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Sandipan Das <sandipan@linux.ibm.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Shakeel Butt <shakeelb@google.com>
Cc: Shuah Khan <shuah@kernel.org>
Link: http://lkml.kernel.org/r/20200211213128.73302-8-almasrymina@google.com
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Mina Almasry and committed by
Linus Torvalds
29750f71 a9b3f867

+1086
+1
tools/testing/selftests/vm/.gitignore
··· 14 14 gup_benchmark 15 15 va_128TBswitch 16 16 map_fixed_noreplace 17 + write_to_hugetlbfs
+1
tools/testing/selftests/vm/Makefile
··· 23 23 ifneq (,$(filter $(ARCH),arm64 ia64 mips64 parisc64 ppc64 riscv64 s390x sh64 sparc64 x86_64)) 24 24 TEST_GEN_FILES += va_128TBswitch 25 25 TEST_GEN_FILES += virtual_address_range 26 + TEST_GEN_FILES += write_to_hugetlbfs 26 27 endif 27 28 28 29 TEST_PROGS := run_vmtests
+575
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
+244
tools/testing/selftests/vm/hugetlb_reparenting_test.sh
··· 1 + #!/bin/bash 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 + usage_file=usage_in_bytes 12 + 13 + if [[ "$1" == "-cgroup-v2" ]]; then 14 + cgroup2=1 15 + usage_file=current 16 + fi 17 + 18 + CGROUP_ROOT='/dev/cgroup/memory' 19 + MNT='/mnt/huge/' 20 + 21 + if [[ ! -e $CGROUP_ROOT ]]; then 22 + mkdir -p $CGROUP_ROOT 23 + if [[ $cgroup2 ]]; then 24 + mount -t cgroup2 none $CGROUP_ROOT 25 + sleep 1 26 + echo "+hugetlb +memory" >$CGROUP_ROOT/cgroup.subtree_control 27 + else 28 + mount -t cgroup memory,hugetlb $CGROUP_ROOT 29 + fi 30 + fi 31 + 32 + function get_machine_hugepage_size() { 33 + hpz=$(grep -i hugepagesize /proc/meminfo) 34 + kb=${hpz:14:-3} 35 + mb=$(($kb / 1024)) 36 + echo $mb 37 + } 38 + 39 + MB=$(get_machine_hugepage_size) 40 + 41 + function cleanup() { 42 + echo cleanup 43 + set +e 44 + rm -rf "$MNT"/* 2>/dev/null 45 + umount "$MNT" 2>/dev/null 46 + rmdir "$MNT" 2>/dev/null 47 + rmdir "$CGROUP_ROOT"/a/b 2>/dev/null 48 + rmdir "$CGROUP_ROOT"/a 2>/dev/null 49 + rmdir "$CGROUP_ROOT"/test1 2>/dev/null 50 + echo 0 >/proc/sys/vm/nr_hugepages 51 + set -e 52 + } 53 + 54 + function assert_state() { 55 + local expected_a="$1" 56 + local expected_a_hugetlb="$2" 57 + local expected_b="" 58 + local expected_b_hugetlb="" 59 + 60 + if [ ! -z ${3:-} ] && [ ! -z ${4:-} ]; then 61 + expected_b="$3" 62 + expected_b_hugetlb="$4" 63 + fi 64 + local tolerance=$((5 * 1024 * 1024)) 65 + 66 + local actual_a 67 + actual_a="$(cat "$CGROUP_ROOT"/a/memory.$usage_file)" 68 + if [[ $actual_a -lt $(($expected_a - $tolerance)) ]] || 69 + [[ $actual_a -gt $(($expected_a + $tolerance)) ]]; then 70 + echo actual a = $((${actual_a%% *} / 1024 / 1024)) MB 71 + echo expected a = $((${expected_a%% *} / 1024 / 1024)) MB 72 + echo fail 73 + 74 + cleanup 75 + exit 1 76 + fi 77 + 78 + local actual_a_hugetlb 79 + actual_a_hugetlb="$(cat "$CGROUP_ROOT"/a/hugetlb.${MB}MB.$usage_file)" 80 + if [[ $actual_a_hugetlb -lt $(($expected_a_hugetlb - $tolerance)) ]] || 81 + [[ $actual_a_hugetlb -gt $(($expected_a_hugetlb + $tolerance)) ]]; then 82 + echo actual a hugetlb = $((${actual_a_hugetlb%% *} / 1024 / 1024)) MB 83 + echo expected a hugetlb = $((${expected_a_hugetlb%% *} / 1024 / 1024)) MB 84 + echo fail 85 + 86 + cleanup 87 + exit 1 88 + fi 89 + 90 + if [[ -z "$expected_b" || -z "$expected_b_hugetlb" ]]; then 91 + return 92 + fi 93 + 94 + local actual_b 95 + actual_b="$(cat "$CGROUP_ROOT"/a/b/memory.$usage_file)" 96 + if [[ $actual_b -lt $(($expected_b - $tolerance)) ]] || 97 + [[ $actual_b -gt $(($expected_b + $tolerance)) ]]; then 98 + echo actual b = $((${actual_b%% *} / 1024 / 1024)) MB 99 + echo expected b = $((${expected_b%% *} / 1024 / 1024)) MB 100 + echo fail 101 + 102 + cleanup 103 + exit 1 104 + fi 105 + 106 + local actual_b_hugetlb 107 + actual_b_hugetlb="$(cat "$CGROUP_ROOT"/a/b/hugetlb.${MB}MB.$usage_file)" 108 + if [[ $actual_b_hugetlb -lt $(($expected_b_hugetlb - $tolerance)) ]] || 109 + [[ $actual_b_hugetlb -gt $(($expected_b_hugetlb + $tolerance)) ]]; then 110 + echo actual b hugetlb = $((${actual_b_hugetlb%% *} / 1024 / 1024)) MB 111 + echo expected b hugetlb = $((${expected_b_hugetlb%% *} / 1024 / 1024)) MB 112 + echo fail 113 + 114 + cleanup 115 + exit 1 116 + fi 117 + } 118 + 119 + function setup() { 120 + echo 100 >/proc/sys/vm/nr_hugepages 121 + mkdir "$CGROUP_ROOT"/a 122 + sleep 1 123 + if [[ $cgroup2 ]]; then 124 + echo "+hugetlb +memory" >$CGROUP_ROOT/a/cgroup.subtree_control 125 + else 126 + echo 0 >$CGROUP_ROOT/a/cpuset.mems 127 + echo 0 >$CGROUP_ROOT/a/cpuset.cpus 128 + fi 129 + 130 + mkdir "$CGROUP_ROOT"/a/b 131 + 132 + if [[ ! $cgroup2 ]]; then 133 + echo 0 >$CGROUP_ROOT/a/b/cpuset.mems 134 + echo 0 >$CGROUP_ROOT/a/b/cpuset.cpus 135 + fi 136 + 137 + mkdir -p "$MNT" 138 + mount -t hugetlbfs none "$MNT" 139 + } 140 + 141 + write_hugetlbfs() { 142 + local cgroup="$1" 143 + local path="$2" 144 + local size="$3" 145 + 146 + if [[ $cgroup2 ]]; then 147 + echo $$ >$CGROUP_ROOT/$cgroup/cgroup.procs 148 + else 149 + echo 0 >$CGROUP_ROOT/$cgroup/cpuset.mems 150 + echo 0 >$CGROUP_ROOT/$cgroup/cpuset.cpus 151 + echo $$ >"$CGROUP_ROOT/$cgroup/tasks" 152 + fi 153 + ./write_to_hugetlbfs -p "$path" -s "$size" -m 0 -o 154 + if [[ $cgroup2 ]]; then 155 + echo $$ >$CGROUP_ROOT/cgroup.procs 156 + else 157 + echo $$ >"$CGROUP_ROOT/tasks" 158 + fi 159 + echo 160 + } 161 + 162 + set -e 163 + 164 + size=$((${MB} * 1024 * 1024 * 25)) # 50MB = 25 * 2MB hugepages. 165 + 166 + cleanup 167 + 168 + echo 169 + echo 170 + echo Test charge, rmdir, uncharge 171 + setup 172 + echo mkdir 173 + mkdir $CGROUP_ROOT/test1 174 + 175 + echo write 176 + write_hugetlbfs test1 "$MNT"/test $size 177 + 178 + echo rmdir 179 + rmdir $CGROUP_ROOT/test1 180 + mkdir $CGROUP_ROOT/test1 181 + 182 + echo uncharge 183 + rm -rf /mnt/huge/* 184 + 185 + cleanup 186 + 187 + echo done 188 + echo 189 + echo 190 + if [[ ! $cgroup2 ]]; then 191 + echo "Test parent and child hugetlb usage" 192 + setup 193 + 194 + echo write 195 + write_hugetlbfs a "$MNT"/test $size 196 + 197 + echo Assert memory charged correctly for parent use. 198 + assert_state 0 $size 0 0 199 + 200 + write_hugetlbfs a/b "$MNT"/test2 $size 201 + 202 + echo Assert memory charged correctly for child use. 203 + assert_state 0 $(($size * 2)) 0 $size 204 + 205 + rmdir "$CGROUP_ROOT"/a/b 206 + sleep 5 207 + echo Assert memory reparent correctly. 208 + assert_state 0 $(($size * 2)) 209 + 210 + rm -rf "$MNT"/* 211 + umount "$MNT" 212 + echo Assert memory uncharged correctly. 213 + assert_state 0 0 214 + 215 + cleanup 216 + fi 217 + 218 + echo 219 + echo 220 + echo "Test child only hugetlb usage" 221 + echo setup 222 + setup 223 + 224 + echo write 225 + write_hugetlbfs a/b "$MNT"/test2 $size 226 + 227 + echo Assert memory charged correctly for child only use. 228 + assert_state 0 $(($size)) 0 $size 229 + 230 + rmdir "$CGROUP_ROOT"/a/b 231 + echo Assert memory reparent correctly. 232 + assert_state 0 $size 233 + 234 + rm -rf "$MNT"/* 235 + umount "$MNT" 236 + echo Assert memory uncharged correctly. 237 + assert_state 0 0 238 + 239 + cleanup 240 + 241 + echo ALL PASS 242 + 243 + umount $CGROUP_ROOT 244 + rm -rf $CGROUP_ROOT
+23
tools/testing/selftests/vm/write_hugetlb_memory.sh
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + set -e 5 + 6 + size=$1 7 + populate=$2 8 + write=$3 9 + cgroup=$4 10 + path=$5 11 + method=$6 12 + private=$7 13 + want_sleep=$8 14 + reserve=$9 15 + 16 + echo "Putting task in cgroup '$cgroup'" 17 + echo $$ > /dev/cgroup/memory/"$cgroup"/cgroup.procs 18 + 19 + echo "Method is $method" 20 + 21 + set +e 22 + ./write_to_hugetlbfs -p "$path" -s "$size" "$write" "$populate" -m "$method" \ 23 + "$private" "$want_sleep" "$reserve"
+242
tools/testing/selftests/vm/write_to_hugetlbfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * This program reserves and uses hugetlb memory, supporting a bunch of 4 + * scenarios needed by the charged_reserved_hugetlb.sh test. 5 + */ 6 + 7 + #include <err.h> 8 + #include <errno.h> 9 + #include <signal.h> 10 + #include <stdio.h> 11 + #include <stdlib.h> 12 + #include <string.h> 13 + #include <unistd.h> 14 + #include <fcntl.h> 15 + #include <sys/types.h> 16 + #include <sys/shm.h> 17 + #include <sys/stat.h> 18 + #include <sys/mman.h> 19 + 20 + /* Global definitions. */ 21 + enum method { 22 + HUGETLBFS, 23 + MMAP_MAP_HUGETLB, 24 + SHM, 25 + MAX_METHOD 26 + }; 27 + 28 + 29 + /* Global variables. */ 30 + static const char *self; 31 + static char *shmaddr; 32 + static int shmid; 33 + 34 + /* 35 + * Show usage and exit. 36 + */ 37 + static void exit_usage(void) 38 + { 39 + printf("Usage: %s -p <path to hugetlbfs file> -s <size to map> " 40 + "[-m <0=hugetlbfs | 1=mmap(MAP_HUGETLB)>] [-l] [-r] " 41 + "[-o] [-w] [-n]\n", 42 + self); 43 + exit(EXIT_FAILURE); 44 + } 45 + 46 + void sig_handler(int signo) 47 + { 48 + printf("Received %d.\n", signo); 49 + if (signo == SIGINT) { 50 + printf("Deleting the memory\n"); 51 + if (shmdt((const void *)shmaddr) != 0) { 52 + perror("Detach failure"); 53 + shmctl(shmid, IPC_RMID, NULL); 54 + exit(4); 55 + } 56 + 57 + shmctl(shmid, IPC_RMID, NULL); 58 + printf("Done deleting the memory\n"); 59 + } 60 + exit(2); 61 + } 62 + 63 + int main(int argc, char **argv) 64 + { 65 + int fd = 0; 66 + int key = 0; 67 + int *ptr = NULL; 68 + int c = 0; 69 + int size = 0; 70 + char path[256] = ""; 71 + enum method method = MAX_METHOD; 72 + int want_sleep = 0, private = 0; 73 + int populate = 0; 74 + int write = 0; 75 + int reserve = 1; 76 + 77 + unsigned long i; 78 + 79 + if (signal(SIGINT, sig_handler) == SIG_ERR) 80 + err(1, "\ncan't catch SIGINT\n"); 81 + 82 + /* Parse command-line arguments. */ 83 + setvbuf(stdout, NULL, _IONBF, 0); 84 + self = argv[0]; 85 + 86 + while ((c = getopt(argc, argv, "s:p:m:owlrn")) != -1) { 87 + switch (c) { 88 + case 's': 89 + size = atoi(optarg); 90 + break; 91 + case 'p': 92 + strncpy(path, optarg, sizeof(path)); 93 + break; 94 + case 'm': 95 + if (atoi(optarg) >= MAX_METHOD) { 96 + errno = EINVAL; 97 + perror("Invalid -m."); 98 + exit_usage(); 99 + } 100 + method = atoi(optarg); 101 + break; 102 + case 'o': 103 + populate = 1; 104 + break; 105 + case 'w': 106 + write = 1; 107 + break; 108 + case 'l': 109 + want_sleep = 1; 110 + break; 111 + case 'r': 112 + private 113 + = 1; 114 + break; 115 + case 'n': 116 + reserve = 0; 117 + break; 118 + default: 119 + errno = EINVAL; 120 + perror("Invalid arg"); 121 + exit_usage(); 122 + } 123 + } 124 + 125 + if (strncmp(path, "", sizeof(path)) != 0) { 126 + printf("Writing to this path: %s\n", path); 127 + } else { 128 + errno = EINVAL; 129 + perror("path not found"); 130 + exit_usage(); 131 + } 132 + 133 + if (size != 0) { 134 + printf("Writing this size: %d\n", size); 135 + } else { 136 + errno = EINVAL; 137 + perror("size not found"); 138 + exit_usage(); 139 + } 140 + 141 + if (!populate) 142 + printf("Not populating.\n"); 143 + else 144 + printf("Populating.\n"); 145 + 146 + if (!write) 147 + printf("Not writing to memory.\n"); 148 + 149 + if (method == MAX_METHOD) { 150 + errno = EINVAL; 151 + perror("-m Invalid"); 152 + exit_usage(); 153 + } else 154 + printf("Using method=%d\n", method); 155 + 156 + if (!private) 157 + printf("Shared mapping.\n"); 158 + else 159 + printf("Private mapping.\n"); 160 + 161 + if (!reserve) 162 + printf("NO_RESERVE mapping.\n"); 163 + else 164 + printf("RESERVE mapping.\n"); 165 + 166 + switch (method) { 167 + case HUGETLBFS: 168 + printf("Allocating using HUGETLBFS.\n"); 169 + fd = open(path, O_CREAT | O_RDWR, 0777); 170 + if (fd == -1) 171 + err(1, "Failed to open file."); 172 + 173 + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, 174 + (private ? MAP_PRIVATE : MAP_SHARED) | 175 + (populate ? MAP_POPULATE : 0) | 176 + (reserve ? 0 : MAP_NORESERVE), 177 + fd, 0); 178 + 179 + if (ptr == MAP_FAILED) { 180 + close(fd); 181 + err(1, "Error mapping the file"); 182 + } 183 + break; 184 + case MMAP_MAP_HUGETLB: 185 + printf("Allocating using MAP_HUGETLB.\n"); 186 + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, 187 + (private ? (MAP_PRIVATE | MAP_ANONYMOUS) : 188 + MAP_SHARED) | 189 + MAP_HUGETLB | (populate ? MAP_POPULATE : 0) | 190 + (reserve ? 0 : MAP_NORESERVE), 191 + -1, 0); 192 + 193 + if (ptr == MAP_FAILED) 194 + err(1, "mmap"); 195 + 196 + printf("Returned address is %p\n", ptr); 197 + break; 198 + case SHM: 199 + printf("Allocating using SHM.\n"); 200 + shmid = shmget(key, size, 201 + SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W); 202 + if (shmid < 0) { 203 + shmid = shmget(++key, size, 204 + SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W); 205 + if (shmid < 0) 206 + err(1, "shmget"); 207 + } 208 + printf("shmid: 0x%x, shmget key:%d\n", shmid, key); 209 + 210 + ptr = shmat(shmid, NULL, 0); 211 + if (ptr == (int *)-1) { 212 + perror("Shared memory attach failure"); 213 + shmctl(shmid, IPC_RMID, NULL); 214 + exit(2); 215 + } 216 + printf("shmaddr: %p\n", ptr); 217 + 218 + break; 219 + default: 220 + errno = EINVAL; 221 + err(1, "Invalid method."); 222 + } 223 + 224 + if (write) { 225 + printf("Writing to memory.\n"); 226 + memset(ptr, 1, size); 227 + } 228 + 229 + if (want_sleep) { 230 + /* Signal to caller that we're done. */ 231 + printf("DONE\n"); 232 + 233 + /* Hold memory until external kill signal is delivered. */ 234 + while (1) 235 + sleep(100); 236 + } 237 + 238 + if (method == HUGETLBFS) 239 + close(fd); 240 + 241 + return 0; 242 + }