• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Out Of Memory when changing cpuset's mems on NUMA. There was a
3  * problem reported upstream that the allocator may see an empty
4  * nodemask when changing cpuset's mems.
5  * http://lkml.org/lkml/2010/5/4/77
6  * http://lkml.org/lkml/2010/5/4/79
7  * http://lkml.org/lkml/2010/5/4/80
8  * This test is based on the reproducers for the above issue.
9  *
10  * Copyright (C) 2010  Red Hat, Inc.
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it would be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18  *
19  * Further, this software is distributed without any warranty that it
20  * is free of the rightful claim of any third person regarding
21  * infringement or the like.  Any license provided herein, whether
22  * implied or otherwise, applies only to this software file.  Patent
23  * licenses, if any, provided herein do not apply to combinations of
24  * this program with other software, or any other product whatsoever.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29  * 02110-1301, USA.
30  */
31 #include "config.h"
32 #include <sys/types.h>
33 #include <sys/mman.h>
34 #include <sys/mount.h>
35 #include <sys/stat.h>
36 #include <sys/wait.h>
37 #include <ctype.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <math.h>
42 #if HAVE_NUMAIF_H
43 #include <numaif.h>
44 #endif
45 #include <signal.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 
51 #include "test.h"
52 #include "mem.h"
53 #include "numa_helper.h"
54 
55 char *TCID = "cpuset01";
56 int TST_TOTAL = 1;
57 
58 #if HAVE_NUMA_H && HAVE_LINUX_MEMPOLICY_H && HAVE_NUMAIF_H \
59 	&& HAVE_MPOL_CONSTANTS
60 volatile int end;
61 static int *nodes;
62 static int nnodes;
63 static long ncpus;
64 
65 static void testcpuset(void);
66 static void sighandler(int signo LTP_ATTRIBUTE_UNUSED);
67 static int mem_hog(void);
68 static int mem_hog_cpuset(int ntasks);
69 static long count_cpu(void);
70 
main(int argc,char * argv[])71 int main(int argc, char *argv[])
72 {
73 
74 	tst_parse_opts(argc, argv, NULL, NULL);
75 
76 	ncpus = count_cpu();
77 	if (get_allowed_nodes_arr(NH_MEMS | NH_CPUS, &nnodes, &nodes) < 0)
78 		tst_brkm(TBROK | TERRNO, NULL, "get_allowed_nodes_arr");
79 	if (nnodes <= 1)
80 		tst_brkm(TCONF, NULL, "requires a NUMA system.");
81 
82 	setup();
83 	testcpuset();
84 	cleanup();
85 	tst_exit();
86 }
87 
testcpuset(void)88 static void testcpuset(void)
89 {
90 	int lc;
91 	int child, i, status;
92 	unsigned long nmask[MAXNODES / BITS_PER_LONG] = { 0 };
93 	char mems[BUFSIZ], buf[BUFSIZ];
94 
95 	read_cpuset_files(CPATH, "cpus", buf);
96 	write_cpuset_files(CPATH_NEW, "cpus", buf);
97 	read_cpuset_files(CPATH, "mems", mems);
98 	write_cpuset_files(CPATH_NEW, "mems", mems);
99 	SAFE_FILE_PRINTF(cleanup, CPATH_NEW "/tasks", "%d", getpid());
100 
101 	switch (child = fork()) {
102 	case -1:
103 		tst_brkm(TBROK | TERRNO, cleanup, "fork");
104 	case 0:
105 		for (i = 0; i < nnodes; i++) {
106 			if (nodes[i] >= MAXNODES)
107 				continue;
108 			set_node(nmask, nodes[i]);
109 		}
110 		if (set_mempolicy(MPOL_BIND, nmask, MAXNODES) == -1)
111 			tst_brkm(TBROK | TERRNO, cleanup, "set_mempolicy");
112 		exit(mem_hog_cpuset(ncpus > 1 ? ncpus : 1));
113 	}
114 	for (lc = 0; TEST_LOOPING(lc); lc++) {
115 		tst_count = 0;
116 		snprintf(buf, BUFSIZ, "%d", nodes[0]);
117 		write_cpuset_files(CPATH_NEW, "mems", buf);
118 		snprintf(buf, BUFSIZ, "%d", nodes[1]);
119 		write_cpuset_files(CPATH_NEW, "mems", buf);
120 	}
121 
122 	if (waitpid(child, &status, WUNTRACED | WCONTINUED) == -1)
123 		tst_brkm(TBROK | TERRNO, cleanup, "waitpid");
124 	if (WEXITSTATUS(status) != 0)
125 		tst_resm(TFAIL, "child exit status is %d", WEXITSTATUS(status));
126 }
127 
setup(void)128 void setup(void)
129 {
130 	tst_require_root();
131 
132 	if (tst_kvercmp(2, 6, 32) < 0)
133 		tst_brkm(TCONF, NULL, "2.6.32 or greater kernel required");
134 
135 	tst_sig(FORK, DEF_HANDLER, cleanup);
136 	TEST_PAUSE;
137 
138 	mount_mem("cpuset", "cpuset", NULL, CPATH, CPATH_NEW);
139 }
140 
cleanup(void)141 void cleanup(void)
142 {
143 	umount_mem(CPATH, CPATH_NEW);
144 }
145 
sighandler(int signo LTP_ATTRIBUTE_UNUSED)146 static void sighandler(int signo LTP_ATTRIBUTE_UNUSED)
147 {
148 	end = 1;
149 }
150 
mem_hog(void)151 static int mem_hog(void)
152 {
153 	long pagesize;
154 	unsigned long *addr;
155 	int ret = 0;
156 
157 	pagesize = getpagesize();
158 	while (!end) {
159 		addr = mmap(NULL, pagesize * 10, PROT_READ | PROT_WRITE,
160 			    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
161 		if (addr == MAP_FAILED) {
162 			ret = 1;
163 			tst_resm(TFAIL | TERRNO, "mmap");
164 			break;
165 		}
166 		memset(addr, 0xF7, pagesize * 10);
167 		munmap(addr, pagesize * 10);
168 	}
169 	return ret;
170 }
171 
mem_hog_cpuset(int ntasks)172 static int mem_hog_cpuset(int ntasks)
173 {
174 	int i, lc, status, ret = 0;
175 	struct sigaction sa;
176 	pid_t *pids;
177 
178 	if (ntasks <= 0)
179 		tst_brkm(TBROK | TERRNO, cleanup, "ntasks is small.");
180 	sa.sa_handler = sighandler;
181 	if (sigemptyset(&sa.sa_mask) < 0)
182 		tst_brkm(TBROK | TERRNO, cleanup, "sigemptyset");
183 	sa.sa_flags = 0;
184 	if (sigaction(SIGUSR1, &sa, NULL) < 0)
185 		tst_brkm(TBROK | TERRNO, cleanup, "sigaction");
186 
187 	pids = malloc(sizeof(pid_t) * ntasks);
188 	if (pids == NULL)
189 		tst_brkm(TBROK | TERRNO, cleanup, "malloc");
190 	for (i = 0; i < ntasks; i++) {
191 		switch (pids[i] = fork()) {
192 		case -1:
193 			tst_resm(TFAIL | TERRNO, "fork %d", pids[i]);
194 			ret = 1;
195 			break;
196 		case 0:
197 			ret = mem_hog();
198 			exit(ret);
199 		default:
200 			break;
201 		}
202 	}
203 	for (lc = 0; TEST_LOOPING(lc); lc++) ;
204 	while (i--) {
205 		if (kill(pids[i], SIGUSR1) == -1) {
206 			tst_resm(TFAIL | TERRNO, "kill %d", pids[i]);
207 			ret = 1;
208 		}
209 	}
210 	while (waitpid(-1, &status, WUNTRACED | WCONTINUED) > 0) {
211 		if (WIFEXITED(status)) {
212 			if (WEXITSTATUS(status) != 0) {
213 				tst_resm(TFAIL, "child exit status is %d",
214 					 WEXITSTATUS(status));
215 				ret = 1;
216 			}
217 		} else if (WIFSIGNALED(status)) {
218 			tst_resm(TFAIL, "child caught signal %d",
219 				 WTERMSIG(status));
220 			ret = 1;
221 		}
222 	}
223 	return ret;
224 }
225 
count_cpu(void)226 static long count_cpu(void)
227 {
228 	int ncpus = 0;
229 
230 	while (path_exist(PATH_SYS_SYSTEM "/cpu/cpu%d", ncpus))
231 		ncpus++;
232 
233 	return ncpus;
234 }
235 
236 #else /* no NUMA */
main(void)237 int main(void)
238 {
239 	tst_brkm(TCONF, NULL, "no NUMA development packages installed.");
240 }
241 #endif
242