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
25if [ "x$(grep -w memory /proc/cgroups | cut -f4)" != "x1" ]; then
26	echo "WARNING:";
27	echo "Either Kernel does not support for memory resource controller or feature not enabled";
28	echo "Skipping all memcgroup testcases....";
29	exit 0
30fi
31
32cd $LTPROOT/testcases/bin
33
34TEST_PATH=$PWD
35PAGESIZE=`./memcg_getpagesize`
36HUGEPAGESIZE=`grep Hugepagesize /proc/meminfo | awk '{ print $2 }'`
37[ -z $HUGEPAGESIZE ] && HUGEPAGESIZE=0
38HUGEPAGESIZE=$(( $HUGEPAGESIZE * 1024 ))
39PASS=0
40FAIL=1
41orig_memory_use_hierarchy=""
42
43cur_id=0
44failed=0
45
46# Record the test result of a test case
47# $1 - The result of the test case, $PASS or $FAIL
48# $2 - The output information
49result()
50{
51	local pass=$1
52	local info="$2"
53
54	if [ $pass -eq $PASS ]; then
55		tst_resm TPASS "$info"
56	else
57		tst_resm TFAIL "$info"
58		: $(( failed += 1 ))
59	fi
60}
61
62# Check size in memcg
63# $1 - Item name
64# $2 - Expected size
65check_mem_stat()
66{
67	if [ -e $1 ]; then
68		item_size=`cat $1`
69	else
70		item_size=`grep -w $1 memory.stat | cut -d " " -f 2`
71	fi
72
73	if [ "$2" = "$item_size" ]; then
74		pass=$PASS
75	else
76		pass=$FAIL
77	fi
78
79	result $pass "$1=$item_size/$2"
80}
81
82warmup()
83{
84	pid=$1
85
86	echo "Warming up for test: $cur_id, pid: $pid"
87	kill -s USR1 $pid 2> /dev/null
88	sleep 1
89	kill -s USR1 $pid 2> /dev/null
90	sleep 1
91
92	kill -0 $pid
93	if [ $? -ne 0 ]; then
94		result $FAIL "cur_id=$cur_id"
95		return 1
96	else
97		echo "Process is still here after warm up: $pid"
98	fi
99
100	return 0
101}
102
103# Run test cases which checks memory.stat after make
104# some memory allocation
105# $1 - the parameters of 'process', such as --shm
106# $2 - the -s parameter of 'process', such as 4096
107# $3 - item name in memory.stat
108# $4 - the expected size
109# $5 - check after free ?
110test_mem_stat()
111{
112	echo "Running $TEST_PATH/memcg_process $1 -s $2"
113	$TEST_PATH/memcg_process $1 -s $2 &
114	sleep 1
115
116	warmup $!
117	if [ $? -ne 0 ]; then
118		return
119	fi
120
121	echo $! > tasks
122	kill -s USR1 $! 2> /dev/null
123	sleep 1
124
125	check_mem_stat $3 $4
126
127	kill -s USR1 $! 2> /dev/null
128	sleep 1
129	if [ $5 -eq 1 ]; then
130		check_mem_stat $3 0
131	fi
132
133	kill -s INT $! 2> /dev/null
134}
135
136# Run test cases which checks memory.max_usage_in_bytes after make
137# some memory allocation
138# $1 - the parameters of 'process', such as --shm
139# $2 - the -s parameter of 'process', such as 4096
140# $3 - item name
141# $4 - the expected size
142# $5 - check after free ?
143test_max_usage_in_bytes()
144{
145	echo "Running $TEST_PATH/memcg_process $1 -s $2"
146	$TEST_PATH/memcg_process $1 -s $2 &
147	sleep 1
148
149	warmup $!
150	if [ $? -ne 0 ]; then
151		return
152	fi
153
154	echo $! > tasks
155	kill -s USR1 $! 2> /dev/null
156	sleep 1
157
158	kill -s USR1 $! 2> /dev/null
159	sleep 1
160
161	check_mem_stat $3 $4
162
163	if [ $5 -eq 1 ]; then
164		echo 0 > $3
165		check_mem_stat $3 0
166	fi
167
168	kill -s INT $! 2> /dev/null
169}
170
171# make some memory allocation
172# $1 - the parameters of 'process', such as --shm
173# $2 - the -s parameter of 'process', such as 4096
174malloc_free_memory()
175{
176	echo "Running $TEST_PATH/memcg_process $1 -s $2"
177	$TEST_PATH/memcg_process $1 -s $2 &
178	sleep 1
179
180	echo $! > tasks
181	kill -s USR1 $! 2> /dev/null
182	sleep 1
183
184	kill -s USR1 $! 2> /dev/null
185	sleep 1
186
187	kill -s INT $! 2> /dev/null
188}
189
190# Test if failcnt > 0, which means page reclamation occured
191# $1 - item name in memcg
192test_failcnt()
193{
194	failcnt=`cat $1`
195	if [ $failcnt -gt 0 ]; then
196		pass=$PASS
197	else
198		pass=$FAIL
199	fi
200
201	result $pass "$1=$failcnt"
202}
203
204# Test process will be killed due to exceed memory limit
205# $1 - the value of memory.limit_in_bytes
206# $2 - the parameters of 'process', such as --shm
207# $3 - the -s parameter of 'process', such as 4096
208# $4 - use mem+swap limitation
209test_proc_kill()
210{
211	echo $1 > memory.limit_in_bytes
212	if [ $4 -eq 1 ]; then
213		if [ -e memory.memsw.limit_in_bytes ]; then
214			echo $1 > memory.memsw.limit_in_bytes
215		else
216			tst_resm TCONF "mem+swap is not enabled"
217			return
218		fi
219	fi
220
221	$TEST_PATH/memcg_process $2 -s $3 &
222	pid=$!
223	sleep 1
224	echo $pid > tasks
225
226	kill -s USR1 $pid 2> /dev/null
227	sleep 1
228
229	ps -p $pid > /dev/null 2> /dev/null
230	if [ $? -ne 0 ]; then
231		wait $pid
232		ret=$?
233		if [ $ret -eq 1 ]; then
234			result $FAIL "process $pid is killed by error"
235		elif [ $ret -eq 2 ]; then
236			result $PASS "Failed to lock memory"
237		else
238			result $PASS "process $pid is killed"
239		fi
240	else
241		kill -s INT $pid 2> /dev/null
242		result $FAIL "process $pid is not killed"
243	fi
244}
245
246# Test limit_in_bytes will be aligned to PAGESIZE
247# $1 - user input value
248# $2 - use mem+swap limitation
249test_limit_in_bytes()
250{
251	echo $1 > memory.limit_in_bytes
252	if [ $2 -eq 1 ]; then
253		if [ -e memory.memsw.limit_in_bytes ]; then
254			echo $1 > memory.memsw.limit_in_bytes
255			limit=`cat memory.memsw.limit_in_bytes`
256		else
257			tst_resm TCONF "mem+swap is not enabled"
258			return
259		fi
260	else
261		limit=`cat memory.limit_in_bytes`
262	fi
263
264	# Kernels prior to 3.19 were rounding up but newer kernels
265	# are rounding down
266	if [ \( $(($PAGESIZE*($1/$PAGESIZE))) -eq $limit \) \
267	    -o \( $(($PAGESIZE*(($1+$PAGESIZE-1)/$PAGESIZE))) -eq $limit \) ]; then
268		result $PASS "input=$1, limit_in_bytes=$limit"
269	else
270		result $FAIL "input=$1, limit_in_bytes=$limit"
271	fi
272}
273
274# Test memory controller doesn't charge hugepage
275# $1 - the value of /proc/sys/vm/nr_hugepages
276# $2 - the parameters of 'process', --mmap-file or --shm
277# $3 - the -s parameter of 'process', such as $HUGEPAGESIZE
278# $4 - 0: expected failure, 1: expected success
279test_hugepage()
280{
281	TMP_FILE=$TEST_PATH/tmp
282	nr_hugepages=`cat /proc/sys/vm/nr_hugepages`
283
284	mkdir /hugetlb
285	mount -t hugetlbfs none /hugetlb
286
287	echo $1 > /proc/sys/vm/nr_hugepages
288
289	$TEST_PATH/memcg_process $2 --hugepage -s $3 > $TMP_FILE 2>&1 &
290	sleep 1
291
292	kill -s USR1 $! 2> /dev/null
293	sleep 1
294
295	check_mem_stat "rss" 0
296
297	echo "TMP_FILE:"
298	cat $TMP_FILE
299
300	if [ $4 -eq 0 ]; then
301		test -s $TMP_FILE
302		if [ $? -eq 0 ]; then
303			result $PASS "allocate hugepage failed as expected"
304		else
305			kill -s USR1 $! 2> /dev/null
306			kill -s INT $! 2> /dev/null
307			result $FAIL "allocate hugepage shoud fail"
308		fi
309	else
310		test ! -s $TMP_FILE
311		if [ $? -eq 0 ]; then
312			kill -s USR1 $! 2> /dev/null
313			kill -s INT $! 2> /dev/null
314			result $PASS "allocate hugepage succeeded"
315		else
316			result $FAIL "allocate hugepage failed"
317		fi
318	fi
319
320	sleep 1
321	rm -rf $TMP_FILE
322	umount /hugetlb
323	rmdir /hugetlb
324	echo $nr_hugepages > /proc/sys/vm/nr_hugepages
325}
326
327# Test the memory charge won't move to subgroup
328# $1 - memory.limit_in_bytes in parent group
329# $2 - memory.limit_in_bytes in sub group
330test_subgroup()
331{
332	mkdir subgroup
333	echo $1 > memory.limit_in_bytes
334	echo $2 > subgroup/memory.limit_in_bytes
335
336	echo "Running $TEST_PATH/memcg_process --mmap-anon -s $PAGESIZE"
337	$TEST_PATH/memcg_process --mmap-anon -s $PAGESIZE &
338	sleep 1
339
340	warmup $!
341	if [ $? -ne 0 ]; then
342		return
343	fi
344
345	echo $! > tasks
346	kill -s USR1 $! 2> /dev/null
347	sleep 1
348	check_mem_stat "rss" $PAGESIZE
349
350	cd subgroup
351	echo $! > tasks
352	check_mem_stat "rss" 0
353
354	# cleanup
355	cd ..
356	echo $! > tasks
357	kill -s INT $! 2> /dev/null
358	sleep 1
359	rmdir subgroup
360}
361
362# Run test cases which test memory.move_charge_at_immigrate
363# $1 - the parameters of 'process', such as --shm
364# $2 - the -s parameter of 'process', such as 4096
365# $3 - some positive value, such as 1
366# $4 - the expected size
367# $5 - the expected size
368test_move_charge()
369{
370	mkdir subgroup_a
371
372	$TEST_PATH/memcg_process $1 -s $2 &
373	sleep 1
374	warmup $!
375	if [ $? -ne 0 ]; then
376		rmdir subgroup_a
377		return
378	fi
379
380	echo $! > subgroup_a/tasks
381	kill -s USR1 $!
382	sleep 1
383
384	mkdir subgroup_b
385	echo $3 > subgroup_b/memory.move_charge_at_immigrate
386	echo $! > subgroup_b/tasks
387
388	cd subgroup_b
389	check_mem_stat "rss" $4
390	check_mem_stat "cache" $5
391	cd ../subgroup_a
392	check_mem_stat "rss" $6
393	check_mem_stat "cache" $7
394
395	cd ..
396	echo $! > tasks
397	kill -s USR1 $!
398	kill -s INT $!
399	sleep 1
400	rmdir subgroup_a subgroup_b
401}
402
403cleanup()
404{
405	if [ -n "$orig_memory_use_hierarchy" ];then
406		echo $orig_memory_use_hierarchy > \
407		     /dev/memcg/memory.use_hierarchy
408		if [ $? -ne 0 ];then
409			tst_resm TINFO "restore "\
410				 "/dev/memcg/memory.use_hierarchy failed"
411		fi
412		orig_memory_use_hierarchy=""
413	fi
414
415	killall -9 memcg_process 2>/dev/null
416	if [ -e /dev/memcg ]; then
417		umount /dev/memcg 2>/dev/null
418		rmdir /dev/memcg 2>/dev/null
419	fi
420}
421
422do_mount()
423{
424	cleanup
425
426	mkdir /dev/memcg 2> /dev/null
427	mount -t cgroup -omemory memcg /dev/memcg
428
429	# The default value for memory.use_hierarchy is 0 and some of tests
430	# (memcg_stat_test.sh and memcg_use_hierarchy_test.sh) expect it so
431	# while there are distributions (RHEL7U0Beta for example) that sets
432	# it to 1.
433	orig_memory_use_hierarchy=$(cat /dev/memcg/memory.use_hierarchy)
434	if [ -z "orig_memory_use_hierarchy" ];then
435		tst_resm TINFO "cat /dev/memcg/memory.use_hierarchy failed"
436	elif [ "$orig_memory_use_hierarchy" = "0" ];then
437		orig_memory_use_hierarchy=""
438	else
439		echo 0 > /dev/memcg/memory.use_hierarchy
440		if [ $? -ne 0 ];then
441			tst_resm TINFO "set /dev/memcg/memory.use_hierarchy" \
442				"to 0 failed"
443		fi
444	fi
445}
446