1 /*
2 * Copyright (C) 2012 Linux Test Project
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it
13 * is free of the rightful claim of any third person regarding
14 * infringement or the like. Any license provided herein, whether
15 * implied or otherwise, applies only to this software file. Patent
16 * licenses, if any, provided herein do not apply to combinations of
17 * this program with other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301, USA.
23 */
24 /*
25 * There are several corner cases (documented in mm/mmap.c) for mbind
26 * vma merge issue, which makes commit 8aacc9f550 slightly incorrect.
27 * KOSAKI Motohiro made a patch for it (commit e26a511) and composed
28 * a reproducer containing these corner cases. Now I port it to LTP.
29 *
30 * Author: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
31 * Ported-to-LTP-by: Caspar Zhang <caspar@casparzhang.com>
32 */
33
34 #include "config.h"
35 #include <sys/types.h>
36 #include <sys/mman.h>
37 #include <errno.h>
38 #if HAVE_NUMA_H
39 #include <numa.h>
40 #endif
41 #if HAVE_NUMAIF_H
42 #include <numaif.h>
43 #endif
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include "test.h"
49 #include "safe_macros.h"
50 #include "numa_helper.h"
51
52 char *TCID = "vma04";
53 int TST_TOTAL = 5;
54
55 #if HAVE_NUMA_H && HAVE_LINUX_MEMPOLICY_H && HAVE_NUMAIF_H \
56 && HAVE_MPOL_CONSTANTS
57 #if defined(LIBNUMA_API_VERSION) && LIBNUMA_API_VERSION == 2
58 static unsigned long pagesize;
59 static int opt_node;
60 static char *opt_nodestr;
61 static char retbuf[BUFSIZ];
62 static void *mmap_addr;
63 static struct bitmask *nmask;
64
65 static option_t options[] = {
66 {"n:", &opt_node, &opt_nodestr},
67 {NULL, NULL, NULL}
68 };
69
70 static void init(void);
71 static void fin(void);
72 static void mem_bind(int index, int len);
73 static void mem_interleave(int index, int len);
74 static void mem_unbind(int index, int len);
75 static void assertion(char *expected, char *value, char *name);
76 static void get_vmas(char *retbuf, void *addr_s, void *addr_e);
77 static void case4(void);
78 static void case5(void);
79 static void case6(void);
80 static void case7(void);
81 static void case8(void);
82 static void setup(void);
83 static void cleanup(void);
84 static void usage(void);
85
main(int argc,char ** argv)86 int main(int argc, char **argv)
87 {
88 int lc, node, err;
89
90 tst_parse_opts(argc, argv, options, usage);
91
92 nmask = numa_allocate_nodemask();
93 if (opt_node) {
94 node = SAFE_STRTOL(NULL, opt_nodestr, 1, LONG_MAX);
95 } else {
96 err = get_allowed_nodes(NH_MEMS | NH_MEMS, 1, &node);
97 if (err == -3)
98 tst_brkm(TCONF, NULL, "requires at least one node.");
99 else if (err < 0)
100 tst_brkm(TBROK | TERRNO, NULL, "get_allowed_nodes");
101 }
102 numa_bitmask_setbit(nmask, node);
103
104 setup();
105
106 for (lc = 0; TEST_LOOPING(lc); lc++) {
107 tst_count = 0;
108
109 case4();
110 case5();
111 case6();
112 case7();
113 case8();
114 }
115
116 cleanup();
117 tst_exit();
118 }
119
120 /*
121 * BBBBBB
122 * AAAAAAAA
123 */
init(void)124 static void init(void)
125 {
126 void *addr;
127
128 addr = SAFE_MMAP(cleanup, NULL, pagesize * 8, PROT_NONE,
129 MAP_ANON | MAP_PRIVATE, 0, 0);
130 SAFE_MMAP(cleanup, addr + pagesize, pagesize * 6,
131 PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, 0,
132 0);
133
134 mmap_addr = addr + pagesize;
135 memset(mmap_addr, 0, pagesize * 6);
136 }
137
fin(void)138 static void fin(void)
139 {
140 void *addr;
141
142 addr = mmap_addr - pagesize;
143 SAFE_MUNMAP(cleanup, addr, pagesize * 8);
144
145 memset(retbuf, 0, sizeof(retbuf));
146 }
147
mem_bind(int index,int len)148 static void mem_bind(int index, int len)
149 {
150 if (mbind(mmap_addr + pagesize * index, pagesize * len,
151 MPOL_BIND, nmask->maskp, nmask->size, 0) != 0) {
152 if (errno != ENOSYS)
153 tst_brkm(TBROK | TERRNO, cleanup, "mbind: bind");
154 else
155 tst_brkm(TCONF, cleanup,
156 "mbind syscall not implemented "
157 "on this system.");
158 }
159 }
160
mem_interleave(int index,int len)161 static void mem_interleave(int index, int len)
162 {
163 if (mbind(mmap_addr + pagesize * index, pagesize * len,
164 MPOL_INTERLEAVE, nmask->maskp, nmask->size, 0) != 0) {
165 if (errno != ENOSYS)
166 tst_brkm(TBROK | TERRNO, cleanup, "mbind: interleave");
167 else
168 tst_brkm(TCONF, cleanup,
169 "mbind syscall not implemented "
170 "on this system.");
171 }
172 }
173
mem_unbind(int index,int len)174 static void mem_unbind(int index, int len)
175 {
176 if (mbind(mmap_addr + pagesize * index, pagesize * len,
177 MPOL_DEFAULT, NULL, 0, 0) != 0) {
178 if (errno != ENOSYS)
179 tst_brkm(TBROK | TERRNO, cleanup, "mbind: unbind");
180 else
181 tst_brkm(TCONF, cleanup,
182 "mbind syscall not implemented "
183 "on this system.");
184 }
185 }
186
assertion(char * expected,char * value,char * name)187 static void assertion(char *expected, char *value, char *name)
188 {
189 if (strcmp(expected, value) == 0)
190 tst_resm(TPASS, "%s: passed.", name);
191 else
192 tst_resm(TFAIL, "%s: failed. expect '%s', actual '%s'",
193 name, expected, value);
194 }
195
get_vmas(char * retbuf,void * addr_s,void * addr_e)196 static void get_vmas(char *retbuf, void *addr_s, void *addr_e)
197 {
198 FILE *fp;
199 void *s, *t;
200 char buf[BUFSIZ], tmpstr[BUFSIZ];
201 int flag;
202
203 retbuf[0] = '\0';
204 flag = 0;
205 fp = fopen("/proc/self/maps", "r");
206 if (fp == NULL)
207 tst_brkm(TBROK | TERRNO, cleanup, "fopen");
208 while (fgets(buf, BUFSIZ, fp) != NULL) {
209 if (sscanf(buf, "%p-%p ", &s, &t) != 2)
210 continue;
211 if (addr_s <= s && s < addr_e) {
212 if (!flag) {
213 sprintf(tmpstr, "%ld", (t - s) / pagesize);
214 flag = 1;
215 } else {
216 sprintf(tmpstr, ",%ld", (t - s) / pagesize);
217 }
218 strncat(retbuf, tmpstr, 32);
219 }
220 }
221 fclose(fp);
222 }
223
224 /*
225 * AAAA
226 * PPPPPPNNNNNN
227 * might become
228 * PPNNNNNNNNNN
229 * case 4 below
230 */
case4(void)231 static void case4(void)
232 {
233 init();
234 mem_bind(0, 4);
235 mem_unbind(2, 2);
236 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6);
237 assertion("2,4", retbuf, "case4");
238 fin();
239 }
240
241 /*
242 * AAAA
243 * PPPPPPNNNNNN
244 * might become
245 * PPPPPPPPPPNN
246 * case 5 below
247 */
case5(void)248 static void case5(void)
249 {
250 init();
251 mem_bind(0, 2);
252 mem_bind(2, 2);
253 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6);
254 assertion("4,2", retbuf, "case5");
255 fin();
256 }
257
258 /*
259 * AAAA
260 * PPPPNNNNXXXX
261 * might become
262 * PPPPPPPPPPPP 6
263 */
case6(void)264 static void case6(void)
265 {
266 init();
267 mem_bind(0, 2);
268 mem_bind(4, 2);
269 mem_bind(2, 2);
270 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6);
271 assertion("6", retbuf, "case6");
272 fin();
273 }
274
275 /*
276 * AAAA
277 * PPPPNNNNXXXX
278 * might become
279 * PPPPPPPPXXXX 7
280 */
case7(void)281 static void case7(void)
282 {
283 init();
284 mem_bind(0, 2);
285 mem_interleave(4, 2);
286 mem_bind(2, 2);
287 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6);
288 assertion("4,2", retbuf, "case7");
289 fin();
290 }
291
292 /*
293 * AAAA
294 * PPPPNNNNXXXX
295 * might become
296 * PPPPNNNNNNNN 8
297 */
case8(void)298 static void case8(void)
299 {
300 init();
301 mem_bind(0, 2);
302 mem_interleave(4, 2);
303 mem_interleave(2, 2);
304 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6);
305 assertion("2,4", retbuf, "case8");
306 fin();
307 }
308
setup(void)309 static void setup(void)
310 {
311 tst_sig(FORK, DEF_HANDLER, cleanup);
312
313 TEST_PAUSE;
314
315 pagesize = getpagesize();
316 }
317
cleanup(void)318 static void cleanup(void)
319 {
320 }
321
usage(void)322 static void usage(void)
323 {
324 printf(" -n Number of NUMA nodes\n");
325 }
326
327 #else /* libnuma v1 */
main(void)328 int main(void)
329 {
330 tst_brkm(TCONF, NULL, "XXX: test is only supported on libnuma v2.");
331 }
332 #endif
333 #else /* no NUMA */
main(void)334 int main(void)
335 {
336 tst_brkm(TCONF, NULL, "no NUMA development packages installed.");
337 }
338 #endif
339