1 /*
2  * Copyright (c) 2017 Fujitsu Ltd.
3  *  Author: Guangwen Feng <fenggw-fnst@cn.fujitsu.com>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program, if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * This is a regression test for a crash caused by memcg function
21  * reentrant on RHEL6.  When doing rmdir(), a pending signal can
22  * interrupt the execution and lead to cgroup_clear_css_refs()
23  * being entered repeatedly, this results in a BUG_ON().
24  *
25  * This bug was introduced by following RHEL6 patch on 2.6.32-488.el6:
26  *
27  *  [mm] memcg: fix race condition between memcg teardown and swapin
28  *  Link: https://bugzilla.redhat.com/show_bug.cgi?id=1001197
29  *  Patch: ftp://partners.redhat.com/1c5d859a/de6aafa8185ed8fd934f2debc72b79eb/kernel-individual-patch/rhel6/v2.6.32-to-kernel-2.6.32-488.el6.tar.bz2
30  *         31675-mm-memcg-fix-race-condition-between-memcg-teardown-.patch
31  *
32  * This test can crash the buggy kernel on RHEL6.6GA, and the bug
33  * was fixed by following patch on 2.6.32-536.el6:
34  *
35  *  [mm] memcg: fix crash in re-entrant cgroup_clear_css_refs()
36  *  Link: https://bugzilla.redhat.com/show_bug.cgi?id=1168185
37  *  Patch: ftp://partners.redhat.com/1c5d859a/de6aafa8185ed8fd934f2debc72b79eb/kernel-individual-patch/rhel6/v2.6.32-to-kernel-2.6.32-536.el6.tar.bz2
38  *         35944-mm-memcg-fix-crash-in-re-entrant-cgroup_clear_css_r.patch
39  */
40 
41 #include <errno.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <sys/types.h>
45 #include <sys/mount.h>
46 #include "tst_test.h"
47 
48 #define MNTPOINT	"memcg"
49 #define SUBDIR	"memcg/testdir"
50 
51 static int mount_flag;
52 static volatile int sigcounter;
53 
sighandler(int sig LTP_ATTRIBUTE_UNUSED)54 static void sighandler(int sig LTP_ATTRIBUTE_UNUSED)
55 {
56 	sigcounter++;
57 }
58 
do_child(void)59 static void do_child(void)
60 {
61 	while (1)
62 		SAFE_KILL(getppid(), SIGUSR1);
63 
64 	exit(0);
65 }
66 
do_test(void)67 static void do_test(void)
68 {
69 	pid_t cpid;
70 
71 	SAFE_SIGNAL(SIGUSR1, sighandler);
72 
73 	cpid = SAFE_FORK();
74 	if (cpid == 0)
75 		do_child();
76 
77 	while (sigcounter < 50000) {
78 		if (access(SUBDIR, F_OK))
79 			SAFE_MKDIR(SUBDIR, 0644);
80 		rmdir(SUBDIR);
81 	}
82 
83 	SAFE_KILL(cpid, SIGKILL);
84 	SAFE_WAIT(NULL);
85 
86 	tst_res(TPASS, "Bug not reproduced");
87 }
88 
setup(void)89 static void setup(void)
90 {
91 	int ret;
92 
93 	SAFE_MKDIR(MNTPOINT, 0644);
94 
95 	ret = mount("memcg", MNTPOINT, "cgroup", 0, "memory");
96 	if (ret) {
97 		if (errno == ENOENT)
98 			tst_brk(TCONF | TERRNO, "memcg not supported");
99 
100 		tst_brk(TCONF | TERRNO, "mounting memcg failed");
101 	}
102 	mount_flag = 1;
103 }
104 
cleanup(void)105 static void cleanup(void)
106 {
107 	if (!access(SUBDIR, F_OK))
108 		SAFE_RMDIR(SUBDIR);
109 
110 	if (mount_flag)
111 		tst_umount(MNTPOINT);
112 }
113 
114 static struct tst_test test = {
115 	.needs_root = 1,
116 	.needs_tmpdir = 1,
117 	.forks_child = 1,
118 	.min_kver = "2.6.24",
119 	.setup = setup,
120 	.cleanup = cleanup,
121 	.test_all = do_test,
122 };
123