#! /bin/bash

################################################################################
##                                                                            ##
## Copyright (c) 2009 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: Li Zefan <lizf@cn.fujitsu.com>                                     ##
##                                                                            ##
################################################################################

cd $LTPROOT/testcases/bin

export TCID="cgroup_regression_test"
export TST_TOTAL=10
export TST_COUNT=1

failed=0

tst_kvercmp 2 6 29
if [ $? -eq 0 ]; then
	tst_brkm TCONF ignored "test must be run with kernel 2.6.29 or newer"
	exit 0
elif [ ! -f /proc/cgroups ]; then
	tst_brkm TCONF ignored "Kernel does not support for control groups; skipping testcases";
	exit 0
elif [ "x$(id -ru)" != x0 ]; then
	tst_brkm TCONF ignored "Test must be run as root"
	exit 0
fi

dmesg -c > /dev/null
nr_bug=`dmesg | grep -c "kernel BUG"`
nr_null=`dmesg | grep -c "kernel NULL pointer dereference"`
nr_warning=`dmesg | grep -c "^WARNING"`
nr_lockdep=`dmesg | grep -c "possible recursive locking detected"`

# check_kernel_bug - check if some kind of kernel bug happened
check_kernel_bug()
{
	new_bug=`dmesg | grep -c "kernel BUG"`
	new_null=`dmesg | grep -c "kernel NULL pointer dereference"`
	new_warning=`dmesg | grep -c "^WARNING"`
	new_lockdep=`dmesg | grep -c "possible recursive locking detected"`

	# no kernel bug is detected
	if [ $new_bug -eq $nr_bug -a $new_warning -eq $nr_warning -a \
	     $new_null -eq $nr_null -a $new_lockdep -eq $nr_lockdep ]; then
		return 1
	fi

	# some kernel bug is detected
	if [ $new_bug -gt $nr_bug ]; then
		tst_resm TFAIL "kernel BUG was detected!"
	fi
	if [ $new_warning -gt $nr_warning ]; then
		tst_resm TFAIL "kernel WARNING was detected!"
	fi
	if [ $new_null -gt $nr_null ]; then
		tst_resm TFAIL "kernel NULL pointer dereference!"
	fi
	if [ $new_lockdep -gt $nr_lockdep ]; then
		tst_resm TFAIL "kernel lockdep warning was detected!"
	fi

	nr_bug=$new_bug
	nr_null=$new_null
	nr_warning=$new_warning
	nr_lockdep=$new_lockdep

	echo "check_kernel_bug found something!"
	dmesg
	failed=1
	return 0
}

#---------------------------------------------------------------------------
# Bug:    There was a race when keeping forking processes and at the same
#         time cat /cgroup/tasks (should be the very first time to read
#         /cgroup/tasks, otherwise this bug won't be triggered)
# Kernel: 2.6.24, 2.6.25-rcX
# Links:  http://lkml.org/lkml/2007/10/17/224
#         http://lkml.org/lkml/2008/3/5/332
#         http://lkml.org/lkml/2008/4/16/493
# Fix:    commit 0e04388f0189fa1f6812a8e1cb6172136eada87e
#---------------------------------------------------------------------------
test_1()
{
	./fork_processes &
	sleep 1

	mount -t cgroup -o none,name=foo cgroup cgroup/
	if [ $? -ne 0 ]; then
		tst_resm TFAIL "failed to mount cgroup filesystem"
		failed=1
		/bin/kill -SIGTERM $!
		return
	fi
	cat cgroup/tasks > /dev/null

	check_kernel_bug
	if [ $? -eq 1 ]; then
		tst_resm TPASS "no kernel bug was found"
	fi

	/bin/kill -SIGTERM $!
	wait $!
	umount cgroup/
}

#---------------------------------------------------------------------------
# Bug:    a cgroup's notify_on_release flag did not inherit from its parent.
# Kernel: 2.6.24-rcX
# Links:  http://lkml.org/lkml/2008/2/25/12
# Fix:    commit bc231d2a048010d5e0b49ac7fddbfa822fc41109
#---------------------------------------------------------------------------
test_2()
{
	mount -t cgroup -o none,name=foo cgroup cgroup/
	if [ $? -ne 0 ]; then
		tst_resm TFAIL "Failed to mount cgroup filesystem"
		failed=1
		return 1
	fi

	echo 0 > cgroup/notify_on_release
	mkdir cgroup/0
	val1=`cat cgroup/0/notify_on_release`

	echo 1 > cgroup/notify_on_release
	mkdir cgroup/1
	val2=`cat cgroup/1/notify_on_release`

	if [ $val1 -ne 0 -o $val2 -ne 1 ]; then
		tst_resm TFAIL "wrong notify_on_release value"
		failed=1
	else
		tst_resm TPASS "notify_on_release is inherited"
	fi

	rmdir cgroup/0 cgroup/1
	umount cgroup/

	return $failed
}

#---------------------------------------------------------------------------
# Bug:    Accessing NULL cgrp->dentry when reading /proc/sched_debug
# Kernel: 2.6.26-2.6.28
# Links:  http://lkml.org/lkml/2008/10/30/44
#         http://lkml.org/lkml/2008/12/12/107
#         http://lkml.org/lkml/2008/12/16/481
# Fix:    commit a47295e6bc42ad35f9c15ac66f598aa24debd4e2
#---------------------------------------------------------------------------
test_3()
{
	if [ ! -e /proc/sched_debug ]; then
		tst_resm TCONF "CONFIG_SCHED_DEBUG is not enabled"
		return
	fi

	grep -q -w "cpu" /proc/cgroups
	if [ $? -ne 0 ]; then
		tst_resm TCONF "CONFIG_CGROUP_SCHED is not enabled"
		return
	fi

	# Run the test for 30 secs
	mount -t cgroup -o cpu xxx cgroup/
	if [ $? -ne 0 ]; then
		tst_resm TFAIL "Failed to mount cpu subsys"
		failed=1
		return
	fi

	./test_3_1.sh &
	pid1=$!
	./test_3_2.sh &
	pid2=$!

	sleep 30
	/bin/kill -SIGUSR1 $pid1 $pid2
	wait $pid1
	wait $pid2

	check_kernel_bug
	if [ $? -eq 1 ]; then
		tst_resm TPASS "no kernel bug was found"
	fi

	rmdir cgroup/* 2> /dev/null
	umount cgroup/
}

#---------------------------------------------------------------------------
# Bug:    cgroup hierarchy lock's lockdep subclass may overflow
# Kernel: 2.6.29-rcX
# Link:   http://lkml.org/lkml/2009/2/4/67
# Fix:
#---------------------------------------------------------------------------
test_4()
{
	if [ ! -e /proc/lockdep ]; then
		tst_resm TCONF "CONFIG_LOCKDEP is not enabled"
		return
	fi

	# MAX_LOCKDEP_SUBCLASSES is 8, so number of subsys should be > 8
	lines=`cat /proc/cgroups | wc -l`
	if [ $lines -le 9 ]; then
		tst_resm TCONF "require more than 8 cgroup subsystems"
		return
	fi

	mount -t cgroup -o none,name=foo cgroup cgroup/
	mkdir cgroup/0
	rmdir cgroup/0
	umount cgroup/

	dmesg | grep -q "MAX_LOCKDEP_SUBCLASSES too low"
	if [ $? -eq 0 ]; then
		tst_resm TFAIL "lockdep BUG was found"
		failed=1
		return
	else
		tst_resm TPASS "no lockdep BUG was found"
	fi
}

#---------------------------------------------------------------------------
# Bug:    When mount cgroup fs and the fs was busy, root_count should not be
#         decremented in cgroup_kill_sb()
# Kernel: 2.6.29-rcX
# Links:  https://openvz.org/pipermail/devel/2009-January/016345.html
#         http://lkml.org/lkml/2009/1/28/190
# Fix:    commit 839ec5452ebfd5905b9c69b20ceb640903a8ea1a
#---------------------------------------------------------------------------
test_5()
{
	lines=`cat /proc/cgroups | wc -l`
	if [ $lines -le 2 ]; then
		tst_resm TCONF "require at least 2 cgroup subsystems"
		return
	fi

	subsys1=`tail -n 1 /proc/cgroups | awk '{ print $1 }'`
	subsys2=`tail -n 2 /proc/cgroups | head -1 | awk '{ print $1 }'`

	mount -t cgroup -o $subsys1,$subsys2 xxx cgroup/
	if [ $? -ne 0 ]; then
		tst_resm TFAIL "mount $subsys1 and $subsys2 failed"
		failed=1
		return
	fi

	# This 2nd mount should fail
	mount -t cgroup -o $subsys1 xxx cgroup/ 2> /dev/null
	if [ $? -eq 0 ]; then
		tst_resm TFAIL "mount $subsys1 should fail"
		umount cgroup/
		failed=1
		return
	fi

	mkdir cgroup/0
	# Otherwise we can't attach task
	if [ "$subsys1" == cpuset -o "$subsys2" == cpuset ]; then
		echo 0 > cgroup/0/cpuset.cpus 2> /dev/null
		echo 0 > cgroup/0/cpuset.mems 2> /dev/null
	fi

	sleep 100 &
	echo $! > cgroup/0/tasks

	check_kernel_bug
	if [ $? -eq 1 ]; then
		tst_resm TPASS "no kernel bug was found"
	fi

	# clean up
	/bin/kill -SIGTERM $! > /dev/null
	wait $!
	rmdir cgroup/0
	umount cgroup/
}

#---------------------------------------------------------------------------
# Bug:    There was a race between cgroup_clone and umount
# Kernel: 2.6.24 - 2.6.28, 2.6.29-rcX
# Links:  http://lkml.org/lkml/2008/12/24/124
# Fix:    commit 7b574b7b0124ed344911f5d581e9bc2d83bbeb19
#---------------------------------------------------------------------------
test_6()
{
	grep -q -w "ns" /proc/cgroups
	if [ $? -ne 0 ]; then
		tst_resm TCONF "CONFIG_CGROUP_NS"
		return
	fi

	# run the test for 30 secs
	./test_6_1.sh &
	pid1=$!
	./test_6_2 &
	pid2=$!

	sleep 30
	/bin/kill -SIGUSR1 $pid1
	/bin/kill -SIGTERM $pid2
	wait $pid1
	wait $pid2

	check_kernel_bug
	if [ $? -eq 1 ]; then
		tst_resm TPASS "no kernel bug was found"
	fi

	# clean up
	mount -t cgroup -o ns xxx cgroup/ > /dev/null 2>&1
	rmdir cgroup/[1-9]* > /dev/null 2>&1
	umount cgroup/
}

#---------------------------------------------------------------------------
# Bug:    There was a bug when remount cgroup fs with some dead subdirs in
#         it (rmdir()ed but still has some refcnts on it). It caused memory
#         leak, and may cause oops when cat /proc/sched_debug.
# Kernel: 2.6.24 - 2.6.27, 2.6.28-rcX
# Links:  http://lkml.org/lkml/2008/12/10/369
# Fix:    commit 307257cf475aac25db30b669987f13d90c934e3a
#---------------------------------------------------------------------------
test_7_1()
{
	mount -t cgroup -o $subsys xxx cgroup/
	if [ $? -ne 0 ]; then
		tst_resm TFAIL "failed to mount $subsys"
		failed=1
		return
	fi

	mkdir cgroup/0
	sleep 100 < cgroup/0 &	# add refcnt to this dir
	rmdir cgroup/0

	# remount with new subsystems added
	# since 2.6.28, this remount will fail
	mount -t cgroup -o remount xxx cgroup/ 2> /dev/null
	/bin/kill -SIGTERM $!
	wait $!
	umount cgroup/
}

test_7_2()
{
	mount -t cgroup -o none,name=foo cgroup cgroup/
	if [ $? -ne 0 ]; then
		tst_resm TFAIL "failed to mount cgroup"
		failed=1
		return
	fi

	mkdir cgroup/0
	sleep 100 < cgroup/0 &	# add refcnt to this dir
	rmdir cgroup/0

	# remount with some subsystems removed
	# since 2.6.28, this remount will fail
	mount -t cgroup -o remount,$subsys xxx cgroup/ 2> /dev/null
	/bin/kill -SIGTERM $!
	wait $!
	umount cgroup/

	# due to the bug, reading /proc/sched_debug may lead to oops
	grep -q -w "cpu" /proc/cgroups
	if [ $? -ne 0 -o ! -e /proc/sched_debug ]; then
		return
	fi

	tmp=0
	while [ $tmp -lt 50 ] ; do
		echo 3 > /proc/sys/vm/drop_caches
		cat /proc/sched_debug > /dev/null
		: $(( tmp += 1 ))
	done
}

test_7()
{
	lines=`cat /proc/cgroups | wc -l`
	if [ $lines -le 2 ]; then
		tst_resm TCONF "require at least 2 cgroup subsystems"
		slt_result $SLT_Untested
		return
	fi

	subsys=`tail -n 1 /proc/cgroups | awk '{ print $1 }'`

	# remount to add new subsystems to the hierarchy
	i=1
	while [ $i -le 2 ] ; do
		test_7_$i
		if [ $? -ne 0 ]; then
			return
		fi

		check_kernel_bug
		if [ $? -eq 0 ]; then
			return
		fi
		: $(( i += 1 ))
	done

	tst_resm TPASS "no kernel bug was found"
}

#---------------------------------------------------------------------------
# Bug:    oops when get cgroupstat of a cgroup control file
# Kernel: 2.6.24 - 2.6.27, 2.6.28-rcX
# Links:  http://lkml.org/lkml/2008/11/19/53
# Fix:    commit 33d283bef23132c48195eafc21449f8ba88fce6b
#---------------------------------------------------------------------------
test_8()
{
	mount -t cgroup -o none,name=foo cgroup cgroup/
	if [ $? -ne 0 ]; then
		tst_resm TFAIL "failed to mount cgroup filesystem"
		failed=1
		return
	fi

	./getdelays -C cgroup/tasks > /dev/null 2>&1
	if [ $? -eq 0 ]; then
		tst_resm TFAIL "should have failed to get cgroupstat of tasks file"
		umount cgroup/
		failed=1
		return
	fi

	check_kernel_bug
	if [ $? -eq 1 ]; then
		tst_resm TPASS "no kernel bug was found"
	fi

	umount cgroup/
}

#---------------------------------------------------------------------------
# Bug:    When running 2 concurrent mount/umount threads, lockdep warning
#         may be triggered, it's a false positive, and it's VFS' issue but
#         not cgroup.
# Kernel: 2.6.24 - 2.6.29-rcX
# Links:  http://lkml.org/lkml/2009/1/4/352
# Fix:    commit ada723dcd681e2dffd7d73345cc8fda0eb0df9bd
#---------------------------------------------------------------------------
test_9()
{
	./test_9_1.sh &
	pid1=$!
	./test_9_2.sh &
	pid2=$!

	sleep 30
	/bin/kill -SIGUSR1 $pid1 $pid2
	wait $pid1
	wait $pid2

	umount cgroup/ 2> /dev/null

	check_kernel_bug
	if [ $? -eq 1 ]; then
		tst_resm TPASS "no kernel warning was found"
	fi
}

#---------------------------------------------------------------------------
# Bug:    When running 2 concurrent mount/umount threads, kernel WARNING
#         may be triggered, but it's VFS' issue but not cgroup.
# Kernel: 2.6.24 - 2.6.29-rcX
# Links:  http://lkml.org/lkml/2009/1/4/354
# Fix:    commit 1a88b5364b535edaa321d70a566e358390ff0872
#---------------------------------------------------------------------------
test_10()
{
	./test_10_1.sh &
	pid1=$!
	./test_10_2.sh &
	pid2=$!

	sleep 30
	/bin/kill -SIGUSR1 $pid1 $pid2
	wait $pid1
	wait $pid2

	mount -t cgroup none cgroup 2> /dev/null
	rmdir cgroup/0
	umount cgroup/

	check_kernel_bug
	if [ $? -eq 1 ]; then
		tst_resm TPASS "no kernel warning was found"
	fi
}

# main

mkdir cgroup/

for ((cur = 1; cur <= $TST_TOTAL; cur++))
{
	export TST_COUNT=$cur

	test_$cur
}

find cgroup/ -maxdepth 1 -depth -exec rmdir {} +

exit $failed
