123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- #! /bin/sh
- ################################################################################
- ## ##
- ## Copyright (c) 2012 FUJITSU LIMITED ##
- ## ##
- ## This program is free software; you can redistribute it and#or modify ##
- ## it under the terms of the GNU General Public License as published by ##
- ## the Free Software Foundation; either version 2 of the License, or ##
- ## (at your option) any later version. ##
- ## ##
- ## This program is distributed in the hope that it will be useful, but ##
- ## WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ##
- ## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ##
- ## for more details. ##
- ## ##
- ## You should have received a copy of the GNU General Public License ##
- ## along with this program; if not, write to the Free Software ##
- ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ##
- ## ##
- ## Author: Peng Haitao <penght@cn.fujitsu.com> ##
- ## ##
- ################################################################################
- TST_NEEDS_CHECKPOINTS=1
- . test.sh
- if [ "x$(grep -w memory /proc/cgroups | cut -f4)" != "x1" ]; then
- tst_brkm TCONF "Kernel does not support the memory resource controller"
- fi
- PAGESIZE=$(tst_getconf PAGESIZE)
- if [ $? -ne 0 ]; then
- tst_brkm TBROK "tst_getconf PAGESIZE failed"
- fi
- # Check for dependencies
- tst_require_cmds killall
- # Post 4.16 kernel updates stat in batch (> 32 pages) every time
- PAGESIZES=$(( $PAGESIZE * 33 ))
- HUGEPAGESIZE=$(awk '/Hugepagesize/ {print $2}' /proc/meminfo)
- [ -z $HUGEPAGESIZE ] && HUGEPAGESIZE=0
- HUGEPAGESIZE=$(( $HUGEPAGESIZE * 1024 ))
- orig_memory_use_hierarchy=""
- MEMSW_USAGE_FLAG=0
- MEMSW_LIMIT_FLAG=0
- tst_tmpdir
- TMP_DIR="$PWD"
- cleanup()
- {
- if [ -n "$LOCAL_CLEANUP" ]; then
- $LOCAL_CLEANUP
- fi
- killall -9 memcg_process 2> /dev/null
- wait
- cd "$TMP_DIR"
- if [ -n "$TEST_ID" -a -d "/dev/memcg/$TEST_ID" ]; then
- for i in "/dev/memcg/$TEST_ID/"*; do
- if [ -d "$i" ]; then
- rmdir "$i"
- fi
- done
- rmdir "/dev/memcg/$TEST_ID"
- fi
- if [ -d "/dev/memcg" ]; then
- umount /dev/memcg
- rmdir /dev/memcg
- fi
- tst_rmdir
- }
- TST_CLEANUP=cleanup
- shmmax_setup()
- {
- tst_require_cmds bc
- shmmax=`cat /proc/sys/kernel/shmmax`
- if [ $(echo "$shmmax < $HUGEPAGESIZE" |bc) -eq 1 ]; then
- ROD echo "$HUGEPAGESIZE" \> /proc/sys/kernel/shmmax
- fi
- }
- shmmax_cleanup()
- {
- if [ -n "$shmmax" ]; then
- echo "$shmmax" > /proc/sys/kernel/shmmax
- fi
- }
- # Check size in memcg
- # $1 - Item name
- # $2 - Expected size
- check_mem_stat()
- {
- if [ -e $1 ]; then
- item_size=`cat $1`
- else
- item_size=`grep -w $1 memory.stat | cut -d " " -f 2`
- fi
- if [ "$2" = "$item_size" ]; then
- tst_resm TPASS "$1 is $2 as expected"
- else
- tst_resm TFAIL "$1 is $item_size, $2 expected"
- fi
- }
- signal_memcg_process()
- {
- local pid=$1
- local size=$2
- local path=$3
- local usage_start=$(cat ${path}memory.usage_in_bytes)
- kill -s USR1 $pid 2> /dev/null
- if [ -z "$size" ]; then
- return
- fi
- local loops=100
- while kill -0 $pid 2> /dev/null; do
- local usage=$(cat ${path}memory.usage_in_bytes)
- local diff_a=$((usage_start - usage))
- local diff_b=$((usage - usage_start))
- if [ "$diff_a" -ge "$size" -o "$diff_b" -ge "$size" ]; then
- return
- fi
- tst_sleep 100ms
- loops=$((loops - 1))
- if [ $loops -le 0 ]; then
- tst_brkm TBROK "timeouted on memory.usage_in_bytes"
- fi
- done
- }
- stop_memcg_process()
- {
- local pid=$1
- kill -s INT $pid 2> /dev/null
- wait $pid
- }
- warmup()
- {
- local pid=$1
- tst_resm TINFO "Warming up pid: $pid"
- signal_memcg_process $pid
- signal_memcg_process $pid
- sleep 1
- kill -0 $pid
- if [ $? -ne 0 ]; then
- wait $pid
- tst_resm TFAIL "Process $pid exited with $? after warm up"
- return 1
- else
- tst_resm TINFO "Process is still here after warm up: $pid"
- fi
- return 0
- }
- # Run test cases which checks memory.stat after make
- # some memory allocation
- test_mem_stat()
- {
- local memtypes="$1"
- local size=$2
- local total_size=$3
- local stat_name=$4
- local exp_stat_size=$5
- local check_after_free=$6
- tst_resm TINFO "Running memcg_process $memtypes -s $size"
- memcg_process $memtypes -s $size &
- TST_CHECKPOINT_WAIT 0
- warmup $!
- if [ $? -ne 0 ]; then
- return
- fi
- echo $! > tasks
- signal_memcg_process $! $size
- check_mem_stat $stat_name $exp_stat_size
- signal_memcg_process $! $size
- if $check_after_free; then
- check_mem_stat $stat_name 0
- fi
- stop_memcg_process $!
- }
- # Run test cases which checks memory.max_usage_in_bytes after make
- # some memory allocation
- # $1 - the parameters of 'process', such as --shm
- # $2 - the -s parameter of 'process', such as 4096
- # $3 - item name
- # $4 - the expected size
- # $5 - check after free ?
- test_max_usage_in_bytes()
- {
- tst_resm TINFO "Running memcg_process $1 -s $2"
- memcg_process $1 -s $2 &
- TST_CHECKPOINT_WAIT 0
- warmup $!
- if [ $? -ne 0 ]; then
- return
- fi
- echo $! > tasks
- signal_memcg_process $! $2
- signal_memcg_process $! $2
- check_mem_stat $3 $4
- if [ $5 -eq 1 ]; then
- echo 0 > $3
- check_mem_stat $3 0
- fi
- stop_memcg_process $!
- }
- # make some memory allocation
- # $1 - the parameters of 'process', such as --shm
- # $2 - the -s parameter of 'process', such as 4096
- malloc_free_memory()
- {
- tst_resm TINFO "Running memcg_process $1 -s $2"
- memcg_process $1 -s $2 &
- TST_CHECKPOINT_WAIT 0
- echo $! > tasks
- signal_memcg_process $! $2
- signal_memcg_process $! $2
- stop_memcg_process $!
- }
- # Test if failcnt > 0, which means page reclamation occured
- # $1 - item name in memcg
- test_failcnt()
- {
- failcnt=`cat $1`
- if [ $failcnt -gt 0 ]; then
- tst_resm TPASS "$1 is $failcnt, > 0 as expected"
- else
- tst_resm TFAIL "$1 is $failcnt, <= 0 expected"
- fi
- }
- # Test process will be killed due to exceed memory limit
- # $1 - the value of memory.limit_in_bytes
- # $2 - the parameters of 'process', such as --shm
- # $3 - the -s parameter of 'process', such as 4096
- # $4 - use mem+swap limitation
- test_proc_kill()
- {
- echo $1 > memory.limit_in_bytes
- if [ $4 -eq 1 ]; then
- if [ -e memory.memsw.limit_in_bytes ]; then
- echo $1 > memory.memsw.limit_in_bytes
- else
- tst_resm TCONF "mem+swap is not enabled"
- return
- fi
- fi
- memcg_process $2 -s $3 &
- pid=$!
- TST_CHECKPOINT_WAIT 0
- echo $pid > tasks
- sleep 1
- signal_memcg_process $pid $3
- tpk_pid_exists=1
- for tpk_iter in $(seq 20); do
- if [ ! -d "/proc/$pid" ] ||
- grep -q 'Z (zombie)' "/proc/$pid/status"; then
- tpk_pid_exists=0
- break
- fi
- tst_sleep 250ms
- done
- if [ $tpk_pid_exists -eq 0 ]; then
- wait $pid
- ret=$?
- if [ $ret -eq 1 ]; then
- tst_resm TFAIL "process $pid is killed by error"
- elif [ $ret -eq 2 ]; then
- tst_resm TPASS "Failed to lock memory"
- else
- tst_resm TPASS "process $pid is killed"
- fi
- else
- stop_memcg_process $!
- tst_resm TFAIL "process $pid is not killed"
- fi
- }
- # Test limit_in_bytes will be aligned to PAGESIZE
- # $1 - user input value
- # $2 - use mem+swap limitation
- test_limit_in_bytes()
- {
- echo $1 > memory.limit_in_bytes
- if [ $2 -eq 1 ]; then
- if [ -e memory.memsw.limit_in_bytes ]; then
- echo $1 > memory.memsw.limit_in_bytes
- limit=`cat memory.memsw.limit_in_bytes`
- else
- tst_resm TCONF "mem+swap is not enabled"
- return
- fi
- else
- limit=`cat memory.limit_in_bytes`
- fi
- # Kernels prior to 3.19 were rounding up but newer kernels
- # are rounding down
- if [ \( $(($PAGESIZE*($1/$PAGESIZE))) -eq $limit \) \
- -o \( $(($PAGESIZE*(($1+$PAGESIZE-1)/$PAGESIZE))) -eq $limit \) ]; then
- tst_resm TPASS "input=$1, limit_in_bytes=$limit"
- else
- tst_resm TFAIL "input=$1, limit_in_bytes=$limit"
- fi
- }
- # Never used, so untested
- #
- # Test memory controller doesn't charge hugepage
- # $1 - the value of /proc/sys/vm/nr_hugepages
- # $2 - the parameters of 'process', --mmap-file or --shm
- # $3 - the -s parameter of 'process', such as $HUGEPAGESIZE
- # $4 - 0: expected failure, 1: expected success
- test_hugepage()
- {
- TMP_FILE="$TMP_DIR/tmp"
- nr_hugepages=`cat /proc/sys/vm/nr_hugepages`
- mkdir /hugetlb
- mount -t hugetlbfs none /hugetlb
- echo $1 > /proc/sys/vm/nr_hugepages
- memcg_process $2 --hugepage -s $3 > $TMP_FILE 2>&1 &
- TST_CHECKPOINT_WAIT 0
- signal_memcg_process $! $3
- check_mem_stat "rss" 0
- echo "TMP_FILE:"
- cat $TMP_FILE
- if [ $4 -eq 0 ]; then
- test -s $TMP_FILE
- if [ $? -eq 0 ]; then
- tst_resm TPASS "allocate hugepage failed as expected"
- else
- signal_memcg_process $! $3
- stop_memcg_process $!
- tst_resm TFAIL "allocate hugepage should fail"
- fi
- else
- test ! -s $TMP_FILE
- if [ $? -eq 0 ]; then
- signal_memcg_process $! $3
- stop_memcg_process $!
- tst_resm TPASS "allocate hugepage succeeded"
- else
- tst_resm TFAIL "allocate hugepage failed"
- fi
- fi
- sleep 1
- rm -rf $TMP_FILE
- umount /hugetlb
- rmdir /hugetlb
- echo $nr_hugepages > /proc/sys/vm/nr_hugepages
- }
- # Test the memory charge won't move to subgroup
- # $1 - memory.limit_in_bytes in parent group
- # $2 - memory.limit_in_bytes in sub group
- test_subgroup()
- {
- mkdir subgroup
- echo $1 > memory.limit_in_bytes
- echo $2 > subgroup/memory.limit_in_bytes
- tst_resm TINFO "Running memcg_process --mmap-anon -s $PAGESIZES"
- memcg_process --mmap-anon -s $PAGESIZES &
- TST_CHECKPOINT_WAIT 0
- warmup $! $PAGESIZES
- if [ $? -ne 0 ]; then
- return
- fi
- echo $! > tasks
- signal_memcg_process $! $PAGESIZES
- check_mem_stat "rss" $PAGESIZES
- cd subgroup
- echo $! > tasks
- check_mem_stat "rss" 0
- # cleanup
- cd ..
- echo $! > tasks
- stop_memcg_process $!
- rmdir subgroup
- }
- # Run test cases which test memory.move_charge_at_immigrate
- test_move_charge()
- {
- local memtypes="$1"
- local size=$2
- local total_size=$3
- local move_charge_mask=$4
- local b_rss=$5
- local b_cache=$6
- local a_rss=$7
- local a_cache=$8
- mkdir subgroup_a
- tst_resm TINFO "Running memcg_process $memtypes -s $size"
- memcg_process $memtypes -s $size &
- TST_CHECKPOINT_WAIT 0
- warmup $!
- if [ $? -ne 0 ]; then
- rmdir subgroup_a
- return
- fi
- echo $! > subgroup_a/tasks
- signal_memcg_process $! $total_size "subgroup_a/"
- mkdir subgroup_b
- echo $move_charge_mask > subgroup_b/memory.move_charge_at_immigrate
- echo $! > subgroup_b/tasks
- cd subgroup_b
- check_mem_stat "rss" $b_rss
- check_mem_stat "cache" $b_cache
- cd ../subgroup_a
- check_mem_stat "rss" $a_rss
- check_mem_stat "cache" $a_cache
- cd ..
- stop_memcg_process $!
- rmdir subgroup_a subgroup_b
- }
- cleanup_test()
- {
- TEST_ID="$1"
- if [ -n "$orig_memory_use_hierarchy" ];then
- echo $orig_memory_use_hierarchy > \
- /dev/memcg/memory.use_hierarchy
- if [ $? -ne 0 ];then
- tst_resm TINFO "restore "\
- "/dev/memcg/memory.use_hierarchy failed"
- fi
- orig_memory_use_hierarchy=""
- fi
- killall -9 memcg_process 2>/dev/null
- wait
- ROD cd "$TMP_DIR"
- ROD rmdir "/dev/memcg/$TEST_ID"
- TEST_ID=""
- ROD umount /dev/memcg
- ROD rmdir /dev/memcg
- }
- setup_test()
- {
- TEST_ID="$1"
- ROD mkdir /dev/memcg
- ROD mount -t cgroup -omemory memcg /dev/memcg
- # The default value for memory.use_hierarchy is 0 and some of tests
- # (memcg_stat_test.sh and memcg_use_hierarchy_test.sh) expect it so
- # while there are distributions (RHEL7U0Beta for example) that sets
- # it to 1.
- orig_memory_use_hierarchy=$(cat /dev/memcg/memory.use_hierarchy)
- if [ -z "$orig_memory_use_hierarchy" ];then
- tst_resm TINFO "cat /dev/memcg/memory.use_hierarchy failed"
- elif [ "$orig_memory_use_hierarchy" = "0" ];then
- orig_memory_use_hierarchy=""
- else
- echo 0 > /dev/memcg/memory.use_hierarchy
- if [ $? -ne 0 ];then
- tst_resm TINFO "set /dev/memcg/memory.use_hierarchy" \
- "to 0 failed"
- fi
- fi
- ROD mkdir "/dev/memcg/$TEST_ID"
- ROD cd "/dev/memcg/$TEST_ID"
- }
- # Run all the test cases
- run_tests()
- {
- for i in $(seq 1 $TST_TOTAL); do
- tst_resm TINFO "Starting test $i"
- setup_test $i
- if [ -e memory.memsw.limit_in_bytes ]; then
- MEMSW_LIMIT_FLAG=1
- fi
- if [ -e memory.memsw.max_usage_in_bytes ]; then
- MEMSW_USAGE_FLAG=1
- fi
- testcase_$i
- cleanup_test $i
- done
- }
|