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