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