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