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