1#! /bin/sh 2# SPDX-License-Identifier: GPL-2.0-or-later 3# Copyright (c) 2012 FUJITSU LIMITED 4# Copyright (c) 2014-2019 Linux Test Project 5# Copyright (c) 2021 Joerg Vehlow <joerg.vehlow@aox-tech.de> 6# 7# Author: Peng Haitao <penght@cn.fujitsu.com> 8 9TST_NEEDS_CHECKPOINTS=1 10TST_NEEDS_ROOT=1 11TST_NEEDS_TMPDIR=1 12TST_NEEDS_CMDS="killall find kill" 13TST_CLEANUP=memcg_cleanup 14TST_SETUP=memcg_setup 15TST_TESTFUNC=memcg_testfunc 16 17MEMCG_SHMMAX=${MEMCG_SHMMAX:-0} 18MEMCG_TESTFUNC=${MEMCG_TESTFUNC:-memcg_no_testfunc} 19 20. cgroup_lib.sh 21 22PAGESIZE=$(tst_getconf PAGESIZE) 23if [ $? -ne 0 ]; then 24 tst_brk TBROK "tst_getconf PAGESIZE failed" 25fi 26 27# Post 4.16 kernel updates stat in batch (> 32 pages) every time 28PAGESIZES=$(($PAGESIZE * 33)) 29 30HUGEPAGESIZE=$(awk '/Hugepagesize/ {print $2}' /proc/meminfo) 31[ -z $HUGEPAGESIZE ] && HUGEPAGESIZE=0 32HUGEPAGESIZE=$(($HUGEPAGESIZE * 1024)) 33 34orig_memory_use_hierarchy= 35orig_shmmax= 36 37memcg_require_memsw() 38{ 39 if ! [ -e /dev/memcg/memory.limit_in_bytes ]; then 40 tst_brk TBROK "/dev/memcg must be mounted before calling memcg_require_memsw" 41 fi 42 if ! [ -e /dev/memcg/memory.memsw.limit_in_bytes ]; then 43 tst_brk TCONF "mem+swap is not enabled" 44 fi 45} 46 47memcg_require_hierarchy_disabled() 48{ 49 if [ ! -e "/dev/memcg/memory.use_hierarchy" ]; then 50 tst_brk TBROK "/dev/memcg must be mounted before calling memcg_require_hierarchy_disabled" 51 fi 52 if [ $(cat /dev/memcg/memory.use_hierarchy) -eq 1 ]; then 53 tst_brk TCONF "Test requires root cgroup memory.use_hierarchy=0" 54 fi 55} 56 57memcg_setup() 58{ 59 if ! is_cgroup_subsystem_available_and_enabled "memory"; then 60 tst_brk TCONF "Either kernel does not support Memory Resource Controller or feature not enabled" 61 fi 62 63 # Setup IPC 64 LTP_IPC_PATH="/dev/shm/ltp_${TCID}_$$" 65 LTP_IPC_SIZE=$PAGESIZE 66 ROD_SILENT dd if=/dev/zero of="$LTP_IPC_PATH" bs="$LTP_IPC_SIZE" count=1 67 ROD_SILENT chmod 600 "$LTP_IPC_PATH" 68 export LTP_IPC_PATH 69 # Setup IPC end 70 71 ROD mkdir /dev/memcg 72 ROD mount -t cgroup -omemory memcg /dev/memcg 73 74 # The default value for memory.use_hierarchy is 0 and some of tests 75 # (memcg_stat_test.sh and memcg_use_hierarchy_test.sh) expect it so 76 # while there are distributions (RHEL7U0Beta for example) that sets 77 # it to 1. 78 # Note: If there are already subgroups created it is not possible, 79 # to set this back to 0. 80 # This seems to be the default for all systems using systemd. 81 orig_memory_use_hierarchy=$(cat /dev/memcg/memory.use_hierarchy) 82 if [ -z "$orig_memory_use_hierarchy" ];then 83 tst_res TINFO "cat /dev/memcg/ failed" 84 elif [ "$orig_memory_use_hierarchy" = "0" ];then 85 orig_memory_use_hierarchy="" 86 else 87 echo 0 > /dev/memcg/memory.use_hierarchy 2>/dev/null 88 if [ $? -ne 0 ];then 89 tst_res TINFO "set /dev/memcg/memory.use_hierarchy to 0 failed" 90 fi 91 fi 92 93 [ "$MEMCG_SHMMAX" = "1" ] && shmmax_setup 94} 95 96memcg_cleanup() 97{ 98 kill -9 $MEMCG_PROCESS_PID 2> /dev/null 99 100 cd $TST_TMPDIR 101 # In order to remove all subgroups, we have to remove them recursively 102 if [ -e /dev/memcg/ltp_$$ ]; then 103 ROD find /dev/memcg/ltp_$$ -depth -type d -delete 104 fi 105 106 if [ -n "$orig_memory_use_hierarchy" ];then 107 echo $orig_memory_use_hierarchy > /dev/memcg/memory.use_hierarchy 108 if [ $? -ne 0 ];then 109 tst_res TINFO "restore /dev/memcg/memory.use_hierarchy failed" 110 fi 111 orig_memory_use_hierarchy="" 112 fi 113 114 if [ -e "/dev/memcg" ]; then 115 umount /dev/memcg 116 rmdir /dev/memcg 117 fi 118 119 [ "$MEMCG_SHMMAX" = "1" ] && shmmax_cleanup 120} 121 122shmmax_setup() 123{ 124 tst_require_cmds bc 125 126 tst_res TINFO "Setting shmmax" 127 128 orig_shmmax=$(cat /proc/sys/kernel/shmmax) 129 if [ $(echo "$orig_shmmax < $HUGEPAGESIZE" | bc) -eq 1 ]; then 130 ROD echo "$HUGEPAGESIZE" \> /proc/sys/kernel/shmmax 131 fi 132} 133 134shmmax_cleanup() 135{ 136 if [ -n "$orig_shmmax" ]; then 137 echo "$orig_shmmax" > /proc/sys/kernel/shmmax 138 fi 139} 140 141# Check size in memcg 142# $1 - Item name 143# $2 - Expected size 144check_mem_stat() 145{ 146 local item_size 147 148 if [ -e $1 ]; then 149 item_size=$(cat $1) 150 else 151 item_size=$(grep -w $1 memory.stat | cut -d " " -f 2) 152 fi 153 154 if [ "$2" = "$item_size" ]; then 155 tst_res TPASS "$1 is $2 as expected" 156 else 157 tst_res TFAIL "$1 is $item_size, $2 expected" 158 fi 159} 160 161start_memcg_process() 162{ 163 tst_res TINFO "Running memcg_process $@" 164 memcg_process "$@" & 165 MEMCG_PROCESS_PID=$! 166 ROD tst_checkpoint wait 10000 0 167} 168 169signal_memcg_process() 170{ 171 local size=$1 172 local path=$2 173 local usage_start=$(cat ${path}memory.usage_in_bytes) 174 175 kill -s USR1 $MEMCG_PROCESS_PID 2> /dev/null 176 177 if [ -z "$size" ]; then 178 return 179 fi 180 181 local loops=100 182 183 while kill -0 $MEMCG_PROCESS_PID 2> /dev/null; do 184 local usage=$(cat ${path}memory.usage_in_bytes) 185 local diff_a=$((usage_start - usage)) 186 local diff_b=$((usage - usage_start)) 187 188 if [ "$diff_a" -ge "$size" -o "$diff_b" -ge "$size" ]; then 189 return 190 fi 191 192 tst_sleep 100ms 193 194 loops=$((loops - 1)) 195 if [ $loops -le 0 ]; then 196 tst_brk TBROK "timed out on memory.usage_in_bytes" 197 fi 198 done 199} 200 201stop_memcg_process() 202{ 203 [ -z "$MEMCG_PROCESS_PID" ] && return 204 kill -s INT $MEMCG_PROCESS_PID 2> /dev/null 205 wait $MEMCG_PROCESS_PID 206 MEMCG_PROCESS_PID= 207} 208 209warmup() 210{ 211 tst_res TINFO "Warming up pid: $MEMCG_PROCESS_PID" 212 signal_memcg_process 213 signal_memcg_process 214 sleep 1 215 216 if ! kill -0 $MEMCG_PROCESS_PID; then 217 wait $MEMCG_PROCESS_PID 218 tst_res TFAIL "Process $MEMCG_PROCESS_PID exited with $? after warm up" 219 return 1 220 else 221 tst_res TINFO "Process is still here after warm up: $MEMCG_PROCESS_PID" 222 fi 223 224 return 0 225} 226 227# Run test cases which checks memory.stat after make 228# some memory allocation 229test_mem_stat() 230{ 231 local memtypes="$1" 232 local size=$2 233 local total_size=$3 234 local stat_name=$4 235 local exp_stat_size=$5 236 local check_after_free=$6 237 238 start_memcg_process $memtypes -s $size 239 240 if ! warmup; then 241 return 242 fi 243 244 echo $MEMCG_PROCESS_PID > tasks 245 signal_memcg_process $size 246 247 check_mem_stat $stat_name $exp_stat_size 248 249 signal_memcg_process $size 250 if $check_after_free; then 251 check_mem_stat $stat_name 0 252 fi 253 254 stop_memcg_process 255} 256 257# Test process will be killed due to exceed memory limit 258# $1 - the value of memory.limit_in_bytes 259# $2 - the parameters of 'process', such as --shm 260# $3 - the -s parameter of 'process', such as 4096 261# $4 - use mem+swap limitation 262test_proc_kill() 263{ 264 local limit=$1 265 local memtypes="$2" 266 local size=$3 267 local use_memsw=$4 268 local tpk_iter 269 270 echo $limit > memory.limit_in_bytes 271 if [ $use_memsw -eq 1 ]; then 272 memcg_require_memsw 273 echo $limit > memory.memsw.limit_in_bytes 274 fi 275 276 start_memcg_process $memtypes -s $size 277 echo $MEMCG_PROCESS_PID > tasks 278 279 signal_memcg_process $size 280 281 local tpk_pid_exists=1 282 for tpk_iter in $(seq 20); do 283 if [ ! -d "/proc/$MEMCG_PROCESS_PID" ] || 284 grep -q 'Z (zombie)' "/proc/$MEMCG_PROCESS_PID/status"; then 285 tpk_pid_exists=0 286 break 287 fi 288 289 tst_sleep 250ms 290 done 291 292 if [ $tpk_pid_exists -eq 0 ]; then 293 wait $MEMCG_PROCESS_PID 294 ret=$? 295 if [ $ret -eq 1 ]; then 296 tst_res TFAIL "process $MEMCG_PROCESS_PID is killed by error" 297 elif [ $ret -eq 2 ]; then 298 tst_res TPASS "Failed to lock memory" 299 else 300 tst_res TPASS "process $MEMCG_PROCESS_PID is killed" 301 fi 302 else 303 stop_memcg_process 304 tst_res TFAIL "process $MEMCG_PROCESS_PID is not killed" 305 fi 306} 307 308# Test limit_in_bytes will be aligned to PAGESIZE 309# $1 - user input value 310# $2 - use mem+swap limitation 311test_limit_in_bytes() 312{ 313 local limit=$1 314 local use_memsw=$2 315 local elimit 316 317 echo $limit > memory.limit_in_bytes 318 if [ $use_memsw -eq 1 ]; then 319 memcg_require_memsw 320 echo $limit > memory.memsw.limit_in_bytes 321 elimit=$(cat memory.memsw.limit_in_bytes) 322 else 323 elimit=$(cat memory.limit_in_bytes) 324 fi 325 326 # Kernels prior to 3.19 were rounding up, 327 # but newer kernels are rounding down 328 local limit_up=$(( PAGESIZE * (limit / PAGESIZE) )) 329 local limit_down=$(( PAGESIZE * ((limit + PAGESIZE - 1) / PAGESIZE) )) 330 if [ $limit_up -eq $elimit ] || [ $limit_down -eq $elimit ]; then 331 tst_res TPASS "input=$limit, limit_in_bytes=$elimit" 332 else 333 tst_res TFAIL "input=$limit, limit_in_bytes=$elimit" 334 fi 335} 336 337memcg_testfunc() 338{ 339 ROD mkdir /dev/memcg/ltp_$$ 340 cd /dev/memcg/ltp_$$ 341 342 if type ${MEMCG_TESTFUNC}1 > /dev/null 2>&1; then 343 ${MEMCG_TESTFUNC}$1 $1 "$2" 344 else 345 ${MEMCG_TESTFUNC} $1 "$2" 346 fi 347 348 cd $TST_TMPDIR 349 ROD rmdir /dev/memcg/ltp_$$ 350} 351 352memcg_no_testfunc() 353{ 354 tst_brk TBROK "No testfunc specified, set MEMCG_TESTFUNC" 355} 356