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