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