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