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 <limits.h>
49#include "test.h"
50#include "safe_macros.h"
51#include "numa_helper.h"
52
53char *TCID = "vma04";
54int TST_TOTAL = 5;
55
56#ifdef HAVE_NUMA_V2
57
58static unsigned long pagesize;
59static int opt_node;
60static char *opt_nodestr;
61static char retbuf[BUFSIZ];
62static void *mmap_addr;
63static struct bitmask *nmask;
64
65static option_t options[] = {
66	{"n:", &opt_node, &opt_nodestr},
67	{NULL, NULL, NULL}
68};
69
70static void init(void);
71static void fin(void);
72static void mem_bind(int index, int len);
73static void mem_interleave(int index, int len);
74static void mem_unbind(int index, int len);
75static void assertion(char *expected, char *value, char *name);
76static void get_vmas(char *retbuf, void *addr_s, void *addr_e);
77static void case4(void);
78static void case5(void);
79static void case6(void);
80static void case7(void);
81static void case8(void);
82static void setup(void);
83static void cleanup(void);
84static void usage(void);
85
86int 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 */
124static 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
138static 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
148static 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
161static 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
174static 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
187static 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
196static 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 */
231static 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 */
248static 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 */
264static 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 */
281static 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 */
298static 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
309static void setup(void)
310{
311	tst_sig(FORK, DEF_HANDLER, cleanup);
312
313	TEST_PAUSE;
314
315	pagesize = getpagesize();
316}
317
318static void cleanup(void)
319{
320}
321
322static void usage(void)
323{
324	printf("  -n      Number of NUMA nodes\n");
325}
326
327#else
328int main(void)
329{
330	tst_brkm(TCONF, NULL, "test requires libnuma >= 2 and it's development packages");
331}
332#endif
333