• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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