• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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