1 #! /bin/sh 2 3 ################################################################################ 4 ## ## 5 ## Copyright (c) 2012 FUJITSU LIMITED ## 6 ## ## 7 ## This program is free software; you can redistribute it and#or modify ## 8 ## it under the terms of the GNU General Public License as published by ## 9 ## the Free Software Foundation; either version 2 of the License, or ## 10 ## (at your option) any later version. ## 11 ## ## 12 ## This program is distributed in the hope that it will be useful, but ## 13 ## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ## 14 ## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ## 15 ## for more details. ## 16 ## ## 17 ## You should have received a copy of the GNU General Public License ## 18 ## along with this program; if not, write to the Free Software ## 19 ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ## 20 ## ## 21 ## Author: Peng Haitao <penght (at] cn.fujitsu.com> ## 22 ## ## 23 ################################################################################ 24 25 TST_NEEDS_CHECKPOINTS=1 26 . test.sh 27 28 if [ "x$(grep -w memory /proc/cgroups | cut -f4)" != "x1" ]; then 29 tst_brkm TCONF "Kernel does not support the memory resource controller" 30 fi 31 32 PAGESIZE=$(getconf PAGESIZE) 33 if [ $? -ne 0 ]; then 34 tst_brkm TBROK "getconf PAGESIZE failed" 35 fi 36 37 HUGEPAGESIZE=$(awk '/Hugepagesize/ {print $2}' /proc/meminfo) 38 [ -z $HUGEPAGESIZE ] && HUGEPAGESIZE=0 39 HUGEPAGESIZE=$(( $HUGEPAGESIZE * 1024 )) 40 orig_memory_use_hierarchy="" 41 42 MEMSW_USAGE_FLAG=0 43 MEMSW_LIMIT_FLAG=0 44 45 tst_tmpdir 46 TMP_DIR="$PWD" 47 48 cleanup() 49 { 50 if [ -n "$LOCAL_CLEANUP" ]; then 51 $LOCAL_CLEANUP 52 fi 53 54 killall -9 memcg_process 2> /dev/null 55 wait 56 57 cd "$TMP_DIR" 58 59 if [ -n "$TEST_ID" -a -d "/dev/memcg/$TEST_ID" ]; then 60 for i in "/dev/memcg/$TEST_ID/"*; do 61 if [ -d "$i" ]; then 62 rmdir "$i" 63 fi 64 done 65 66 rmdir "/dev/memcg/$TEST_ID" 67 fi 68 69 if [ -d "/dev/memcg" ]; then 70 umount /dev/memcg 71 rmdir /dev/memcg 72 fi 73 74 tst_rmdir 75 } 76 TST_CLEANUP=cleanup 77 78 shmmax_setup() 79 { 80 shmmax=`cat /proc/sys/kernel/shmmax` 81 if [ $shmmax -lt $HUGEPAGESIZE ]; then 82 ROD echo "$HUGEPAGESIZE" \> /proc/sys/kernel/shmmax 83 fi 84 } 85 86 shmmax_cleanup() 87 { 88 if [ -n "$shmmax" ]; then 89 echo "$shmmax" > /proc/sys/kernel/shmmax 90 fi 91 } 92 93 # Check size in memcg 94 # $1 - Item name 95 # $2 - Expected size 96 check_mem_stat() 97 { 98 if [ -e $1 ]; then 99 item_size=`cat $1` 100 else 101 item_size=`grep -w $1 memory.stat | cut -d " " -f 2` 102 fi 103 104 if [ "$2" = "$item_size" ]; then 105 tst_resm TPASS "$1 is $2 as expected" 106 else 107 tst_resm TFAIL "$1 is $item_size, $2 expected" 108 fi 109 } 110 111 signal_memcg_process() 112 { 113 local pid=$1 114 local size=$2 115 local path=$3 116 local usage_start=$(cat ${path}memory.usage_in_bytes) 117 118 kill -s USR1 $pid 2> /dev/null 119 120 if [ -z "$size" ]; then 121 return 122 fi 123 124 local loops=100 125 126 while kill -0 $pid 2> /dev/null; do 127 local usage=$(cat ${path}memory.usage_in_bytes) 128 local diff_a=$((usage_start - usage)) 129 local diff_b=$((usage - usage_start)) 130 131 if [ "$diff_a" -ge "$size" -o "$diff_b" -ge "$size" ]; then 132 return 133 fi 134 135 tst_sleep 100ms 136 137 loops=$((loops - 1)) 138 if [ $loops -le 0 ]; then 139 tst_brkm TBROK "timeouted on memory.usage_in_bytes" 140 fi 141 done 142 } 143 144 stop_memcg_process() 145 { 146 local pid=$1 147 kill -s INT $pid 2> /dev/null 148 wait $pid 149 } 150 151 warmup() 152 { 153 local pid=$1 154 155 tst_resm TINFO "Warming up pid: $pid" 156 signal_memcg_process $pid 157 signal_memcg_process $pid 158 sleep 1 159 160 kill -0 $pid 161 if [ $? -ne 0 ]; then 162 wait $pid 163 tst_resm TFAIL "Process $pid exited with $? after warm up" 164 return 1 165 else 166 tst_resm TINFO "Process is still here after warm up: $pid" 167 fi 168 169 return 0 170 } 171 172 # Run test cases which checks memory.stat after make 173 # some memory allocation 174 test_mem_stat() 175 { 176 local memtypes="$1" 177 local size=$2 178 local total_size=$3 179 local stat_name=$4 180 local exp_stat_size=$5 181 local check_after_free=$6 182 183 tst_resm TINFO "Running memcg_process $memtypes -s $size" 184 memcg_process $memtypes -s $size & 185 TST_CHECKPOINT_WAIT 0 186 187 warmup $! 188 if [ $? -ne 0 ]; then 189 return 190 fi 191 192 echo $! > tasks 193 signal_memcg_process $! $size 194 195 check_mem_stat $stat_name $exp_stat_size 196 197 signal_memcg_process $! $size 198 if $check_after_free; then 199 check_mem_stat $stat_name 0 200 fi 201 202 stop_memcg_process $! 203 } 204 205 # Run test cases which checks memory.max_usage_in_bytes after make 206 # some memory allocation 207 # $1 - the parameters of 'process', such as --shm 208 # $2 - the -s parameter of 'process', such as 4096 209 # $3 - item name 210 # $4 - the expected size 211 # $5 - check after free ? 212 test_max_usage_in_bytes() 213 { 214 tst_resm TINFO "Running memcg_process $1 -s $2" 215 memcg_process $1 -s $2 & 216 TST_CHECKPOINT_WAIT 0 217 218 warmup $! 219 if [ $? -ne 0 ]; then 220 return 221 fi 222 223 echo $! > tasks 224 signal_memcg_process $! $2 225 signal_memcg_process $! $2 226 227 check_mem_stat $3 $4 228 229 if [ $5 -eq 1 ]; then 230 echo 0 > $3 231 check_mem_stat $3 0 232 fi 233 234 stop_memcg_process $! 235 } 236 237 # make some memory allocation 238 # $1 - the parameters of 'process', such as --shm 239 # $2 - the -s parameter of 'process', such as 4096 240 malloc_free_memory() 241 { 242 tst_resm TINFO "Running memcg_process $1 -s $2" 243 memcg_process $1 -s $2 & 244 TST_CHECKPOINT_WAIT 0 245 246 echo $! > tasks 247 signal_memcg_process $! $2 248 signal_memcg_process $! $2 249 250 stop_memcg_process $! 251 } 252 253 # Test if failcnt > 0, which means page reclamation occured 254 # $1 - item name in memcg 255 test_failcnt() 256 { 257 failcnt=`cat $1` 258 if [ $failcnt -gt 0 ]; then 259 tst_resm TPASS "$1 is $failcnt, > 0 as expected" 260 else 261 tst_resm TFAIL "$1 is $failcnt, <= 0 expected" 262 fi 263 } 264 265 # Test process will be killed due to exceed memory limit 266 # $1 - the value of memory.limit_in_bytes 267 # $2 - the parameters of 'process', such as --shm 268 # $3 - the -s parameter of 'process', such as 4096 269 # $4 - use mem+swap limitation 270 test_proc_kill() 271 { 272 echo $1 > memory.limit_in_bytes 273 if [ $4 -eq 1 ]; then 274 if [ -e memory.memsw.limit_in_bytes ]; then 275 echo $1 > memory.memsw.limit_in_bytes 276 else 277 tst_resm TCONF "mem+swap is not enabled" 278 return 279 fi 280 fi 281 282 memcg_process $2 -s $3 & 283 pid=$! 284 TST_CHECKPOINT_WAIT 0 285 echo $pid > tasks 286 287 signal_memcg_process $pid $3 288 289 tpk_pid_exists=1 290 for tpk_iter in $(seq 20); do 291 if [ ! -d "/proc/$pid" ] || 292 grep -q 'Z (zombie)' "/proc/$pid/status"; then 293 tpk_pid_exists=0 294 break 295 fi 296 297 tst_sleep 250ms 298 done 299 300 if [ $tpk_pid_exists -eq 0 ]; then 301 wait $pid 302 ret=$? 303 if [ $ret -eq 1 ]; then 304 tst_resm TFAIL "process $pid is killed by error" 305 elif [ $ret -eq 2 ]; then 306 tst_resm TPASS "Failed to lock memory" 307 else 308 tst_resm TPASS "process $pid is killed" 309 fi 310 else 311 stop_memcg_process $! 312 tst_resm TFAIL "process $pid is not killed" 313 fi 314 } 315 316 # Test limit_in_bytes will be aligned to PAGESIZE 317 # $1 - user input value 318 # $2 - use mem+swap limitation 319 test_limit_in_bytes() 320 { 321 echo $1 > memory.limit_in_bytes 322 if [ $2 -eq 1 ]; then 323 if [ -e memory.memsw.limit_in_bytes ]; then 324 echo $1 > memory.memsw.limit_in_bytes 325 limit=`cat memory.memsw.limit_in_bytes` 326 else 327 tst_resm TCONF "mem+swap is not enabled" 328 return 329 fi 330 else 331 limit=`cat memory.limit_in_bytes` 332 fi 333 334 # Kernels prior to 3.19 were rounding up but newer kernels 335 # are rounding down 336 if [ \( $(($PAGESIZE*($1/$PAGESIZE))) -eq $limit \) \ 337 -o \( $(($PAGESIZE*(($1+$PAGESIZE-1)/$PAGESIZE))) -eq $limit \) ]; then 338 tst_resm TPASS "input=$1, limit_in_bytes=$limit" 339 else 340 tst_resm TFAIL "input=$1, limit_in_bytes=$limit" 341 fi 342 } 343 344 # Never used, so untested 345 # 346 # Test memory controller doesn't charge hugepage 347 # $1 - the value of /proc/sys/vm/nr_hugepages 348 # $2 - the parameters of 'process', --mmap-file or --shm 349 # $3 - the -s parameter of 'process', such as $HUGEPAGESIZE 350 # $4 - 0: expected failure, 1: expected success 351 test_hugepage() 352 { 353 TMP_FILE="$TMP_DIR/tmp" 354 nr_hugepages=`cat /proc/sys/vm/nr_hugepages` 355 356 mkdir /hugetlb 357 mount -t hugetlbfs none /hugetlb 358 359 echo $1 > /proc/sys/vm/nr_hugepages 360 361 memcg_process $2 --hugepage -s $3 > $TMP_FILE 2>&1 & 362 TST_CHECKPOINT_WAIT 0 363 364 signal_memcg_process $! $3 365 366 check_mem_stat "rss" 0 367 368 echo "TMP_FILE:" 369 cat $TMP_FILE 370 371 if [ $4 -eq 0 ]; then 372 test -s $TMP_FILE 373 if [ $? -eq 0 ]; then 374 tst_resm TPASS "allocate hugepage failed as expected" 375 else 376 signal_memcg_process $! $3 377 stop_memcg_process $! 378 tst_resm TFAIL "allocate hugepage should fail" 379 fi 380 else 381 test ! -s $TMP_FILE 382 if [ $? -eq 0 ]; then 383 signal_memcg_process $! $3 384 stop_memcg_process $! 385 tst_resm TPASS "allocate hugepage succeeded" 386 else 387 tst_resm TFAIL "allocate hugepage failed" 388 fi 389 fi 390 391 sleep 1 392 rm -rf $TMP_FILE 393 umount /hugetlb 394 rmdir /hugetlb 395 echo $nr_hugepages > /proc/sys/vm/nr_hugepages 396 } 397 398 # Test the memory charge won't move to subgroup 399 # $1 - memory.limit_in_bytes in parent group 400 # $2 - memory.limit_in_bytes in sub group 401 test_subgroup() 402 { 403 mkdir subgroup 404 echo $1 > memory.limit_in_bytes 405 echo $2 > subgroup/memory.limit_in_bytes 406 407 tst_resm TINFO "Running memcg_process --mmap-anon -s $PAGESIZE" 408 memcg_process --mmap-anon -s $PAGESIZE & 409 TST_CHECKPOINT_WAIT 0 410 411 warmup $! $PAGESIZE 412 if [ $? -ne 0 ]; then 413 return 414 fi 415 416 echo $! > tasks 417 signal_memcg_process $! $PAGESIZE 418 check_mem_stat "rss" $PAGESIZE 419 420 cd subgroup 421 echo $! > tasks 422 check_mem_stat "rss" 0 423 424 # cleanup 425 cd .. 426 echo $! > tasks 427 stop_memcg_process $! 428 rmdir subgroup 429 } 430 431 # Run test cases which test memory.move_charge_at_immigrate 432 test_move_charge() 433 { 434 local memtypes="$1" 435 local size=$2 436 local total_size=$3 437 local move_charge_mask=$4 438 local b_rss=$5 439 local b_cache=$6 440 local a_rss=$7 441 local a_cache=$8 442 443 mkdir subgroup_a 444 445 tst_resm TINFO "Running memcg_process $memtypes -s $size" 446 memcg_process $memtypes -s $size & 447 TST_CHECKPOINT_WAIT 0 448 warmup $! 449 if [ $? -ne 0 ]; then 450 rmdir subgroup_a 451 return 452 fi 453 454 echo $! > subgroup_a/tasks 455 signal_memcg_process $! $total_size "subgroup_a/" 456 457 mkdir subgroup_b 458 echo $move_charge_mask > subgroup_b/memory.move_charge_at_immigrate 459 echo $! > subgroup_b/tasks 460 461 cd subgroup_b 462 check_mem_stat "rss" $b_rss 463 check_mem_stat "cache" $b_cache 464 cd ../subgroup_a 465 check_mem_stat "rss" $a_rss 466 check_mem_stat "cache" $a_cache 467 cd .. 468 stop_memcg_process $! 469 rmdir subgroup_a subgroup_b 470 } 471 472 cleanup_test() 473 { 474 TEST_ID="$1" 475 476 if [ -n "$orig_memory_use_hierarchy" ];then 477 echo $orig_memory_use_hierarchy > \ 478 /dev/memcg/memory.use_hierarchy 479 if [ $? -ne 0 ];then 480 tst_resm TINFO "restore "\ 481 "/dev/memcg/memory.use_hierarchy failed" 482 fi 483 orig_memory_use_hierarchy="" 484 fi 485 486 killall -9 memcg_process 2>/dev/null 487 wait 488 489 ROD cd "$TMP_DIR" 490 491 ROD rmdir "/dev/memcg/$TEST_ID" 492 TEST_ID="" 493 ROD umount /dev/memcg 494 ROD rmdir /dev/memcg 495 } 496 497 setup_test() 498 { 499 TEST_ID="$1" 500 501 ROD mkdir /dev/memcg 502 ROD mount -t cgroup -omemory memcg /dev/memcg 503 504 # The default value for memory.use_hierarchy is 0 and some of tests 505 # (memcg_stat_test.sh and memcg_use_hierarchy_test.sh) expect it so 506 # while there are distributions (RHEL7U0Beta for example) that sets 507 # it to 1. 508 orig_memory_use_hierarchy=$(cat /dev/memcg/memory.use_hierarchy) 509 if [ -z "orig_memory_use_hierarchy" ];then 510 tst_resm TINFO "cat /dev/memcg/memory.use_hierarchy failed" 511 elif [ "$orig_memory_use_hierarchy" = "0" ];then 512 orig_memory_use_hierarchy="" 513 else 514 echo 0 > /dev/memcg/memory.use_hierarchy 515 if [ $? -ne 0 ];then 516 tst_resm TINFO "set /dev/memcg/memory.use_hierarchy" \ 517 "to 0 failed" 518 fi 519 fi 520 521 ROD mkdir "/dev/memcg/$TEST_ID" 522 ROD cd "/dev/memcg/$TEST_ID" 523 } 524 525 # Run all the test cases 526 run_tests() 527 { 528 for i in $(seq 1 $TST_TOTAL); do 529 530 tst_resm TINFO "Starting test $i" 531 532 setup_test $i 533 534 if [ -e memory.memsw.limit_in_bytes ]; then 535 MEMSW_LIMIT_FLAG=1 536 fi 537 538 if [ -e memory.memsw.max_usage_in_bytes ]; then 539 MEMSW_USAGE_FLAG=1 540 fi 541 542 testcase_$i 543 544 cleanup_test $i 545 done 546 } 547