• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *   Copyright (c) 2008 Vijay Kumar B. <vijaykumar@bravegnu.org>
3  *
4  *   This program is free software;  you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12  *   the GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program;  if not, write to the Free Software
16  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include "config.h"
20 #include <sys/mman.h>
21 #include <sys/syscall.h>
22 #include <unistd.h>
23 #include <semaphore.h>
24 #include "test.h"
25 #include "move_pages_support.h"
26 
get_page_size(void)27 long get_page_size(void)
28 {
29 	return sysconf(_SC_PAGESIZE);
30 }
31 
32 /*
33  * free_pages() - free an array of pages
34  * @pages: array of page pointers to be freed
35  * @num: no. of pages in the array
36  */
free_pages(void ** pages,unsigned int num)37 void free_pages(void **pages, unsigned int num)
38 {
39 #ifdef HAVE_NUMA_V2
40 	int i;
41 	size_t onepage = get_page_size();
42 
43 	for (i = 0; i < num; i++) {
44 		if (pages[i] != NULL) {
45 			numa_free(pages[i], onepage);
46 		}
47 	}
48 #endif
49 }
50 
51 /*
52  * alloc_pages_on_nodes() - allocate pages on specified NUMA nodes
53  * @pages: array in which the page pointers will be stored
54  * @num: no. of pages to allocate
55  * @nodes: array of NUMA nodes
56  *
57  * A page will be allocated in each node specified by @nodes, and the
58  * page pointers will be stored in @pages array.
59  *
60  * RETURNS:
61  * 0 on success, -1 on allocation failure.
62  */
alloc_pages_on_nodes(void ** pages,unsigned int num,int * nodes)63 int alloc_pages_on_nodes(void **pages, unsigned int num, int *nodes)
64 {
65 	int i;
66 #ifdef HAVE_NUMA_V2
67 	size_t onepage = get_page_size();
68 #endif
69 
70 	for (i = 0; i < (int)num; i++) {
71 		pages[i] = NULL;
72 	}
73 
74 	for (i = 0; i < (int)num; i++) {
75 		char *page;
76 
77 #ifdef HAVE_NUMA_V2
78 		pages[i] = numa_alloc_onnode(onepage, nodes[i]);
79 #endif
80 		if (pages[i] == NULL) {
81 			tst_resm(TBROK, "allocation of page on node "
82 				 "%d failed", nodes[i]);
83 			break;
84 		}
85 
86 		/* Touch the page, to force allocation. */
87 		page = pages[i];
88 		page[0] = i;
89 	}
90 
91 	if (i == (int)num)
92 		return 0;
93 
94 	free_pages(pages, num);
95 
96 	return -1;
97 }
98 
99 /*
100  * alloc_pages_linear() - allocate pages in each NUMA node
101  * @pages: array in which the page pointers will be stored
102  * @num: no. of pages to allocate
103  *
104  * Pages will be allocated one from each NUMA node, in an interleaved
105  * fashion.
106  *
107  * RETURNS:
108  * 0 on success, -1 on allocation failure.
109  */
alloc_pages_linear(void ** pages,unsigned int num)110 int alloc_pages_linear(void **pages, unsigned int num)
111 {
112 	int nodes[num];
113 
114 #ifdef HAVE_NUMA_V2
115 	unsigned int i;
116 	unsigned int n = 0;
117 	int num_allowed_nodes;
118 	int *allowed_nodes;
119 	int ret;
120 
121 	ret = get_allowed_nodes_arr(NH_MEMS, &num_allowed_nodes,
122 				    &allowed_nodes);
123 	if (ret < 0)
124 		tst_brkm(TBROK | TERRNO, NULL, "get_allowed_nodes(): %d", ret);
125 
126 	for (i = 0; i < num; i++) {
127 		nodes[i] = allowed_nodes[n];
128 		n++;
129 		if (n >= num_allowed_nodes)
130 			n = 0;
131 	}
132 	free(allowed_nodes);
133 #endif
134 
135 	return alloc_pages_on_nodes(pages, num, nodes);
136 }
137 
138 /*
139  * alloc_pages_on_node() - allocate pages on specified NUMA node
140  * @pages: array in which the page pointers will be stored
141  * @num: no. of pages to allocate
142  * @node: NUMA node from which to allocate pages
143  *
144  * Pages will be allocated from the NUMA node @node, and the page
145  * pointers will be stored in the @pages array.
146  *
147  * RETURNS:
148  * 0 on success, -1 on allocation failure.
149  */
alloc_pages_on_node(void ** pages,unsigned int num,int node)150 int alloc_pages_on_node(void **pages, unsigned int num, int node)
151 {
152 	unsigned int i;
153 	int nodes[num];
154 
155 	for (i = 0; i < num; i++)
156 		nodes[i] = node;
157 
158 	return alloc_pages_on_nodes(pages, num, nodes);
159 }
160 
161 /*
162  * verify_pages_on_nodes() - verify pages are in specified nodes
163  * @pages: array of pages to be verified
164  * @status: the NUMA node of each page
165  * @num: the no. of pages
166  * @nodes: the expected NUMA nodes
167  */
168 void
verify_pages_on_nodes(void ** pages,int * status,unsigned int num,int * nodes)169 verify_pages_on_nodes(void **pages, int *status, unsigned int num, int *nodes)
170 {
171 #ifdef HAVE_NUMA_V2
172 	unsigned int i;
173 	int which_node;
174 	int ret;
175 
176 	for (i = 0; i < num; i++) {
177 		if (status[i] != nodes[i]) {
178 			tst_resm(TFAIL, "page %d on node %d, "
179 				 "expected on node %d", i, status[i], nodes[i]);
180 			return;
181 		}
182 
183 		/* Based on inputs from Andi Kleen.
184 		 *
185 		 * Retrieves numa node for the given page. This does
186 		 * not seem to be documented in the man pages.
187 		 */
188 		ret = get_mempolicy(&which_node, NULL, 0,
189 				    pages[i], MPOL_F_NODE | MPOL_F_ADDR);
190 		if (ret == -1) {
191 			tst_resm(TBROK | TERRNO, "error getting memory policy "
192 				 "for page %p", pages[i]);
193 			return;
194 		}
195 
196 		if (which_node != nodes[i]) {
197 			tst_resm(TFAIL, "page %p is not in node %d ",
198 				 pages[i], nodes[i]);
199 			return;
200 		}
201 	}
202 
203 	tst_resm(TPASS, "pages are present in expected nodes");
204 #else
205 	tst_resm(TCONF, "NUMA support not provided");
206 #endif
207 }
208 
209 /*
210  * verify_pages_linear() - verify pages are interleaved
211  * @pages: array of pages to be verified
212  * @status: the NUMA node of each page
213  * @num: the no. of pages
214  */
verify_pages_linear(void ** pages,int * status,unsigned int num)215 void verify_pages_linear(void **pages, int *status, unsigned int num)
216 {
217 #ifdef HAVE_NUMA_V2
218 	unsigned int i;
219 	unsigned int n = 0;
220 	int nodes[num];
221 	int num_allowed_nodes;
222 	int *allowed_nodes;
223 	int ret;
224 
225 	ret = get_allowed_nodes_arr(NH_MEMS, &num_allowed_nodes,
226 				    &allowed_nodes);
227 	if (ret < 0)
228 		tst_brkm(TBROK | TERRNO, NULL, "get_allowed_nodes(): %d", ret);
229 
230 	for (i = 0; i < num; i++) {
231 		nodes[i] = allowed_nodes[n];
232 		n++;
233 		if (n >= num_allowed_nodes)
234 			n = 0;
235 	}
236 	free(allowed_nodes);
237 
238 	verify_pages_on_nodes(pages, status, num, nodes);
239 #endif
240 }
241 
242 /*
243  * verify_pages_on_node() - verify pages are in specified node
244  * @pages: array of pages to be verified
245  * @status: the NUMA node of each page
246  * @num: the no. of pages
247  * @node: the expected NUMA node
248  */
verify_pages_on_node(void ** pages,int * status,unsigned int num,int node)249 void verify_pages_on_node(void **pages, int *status, unsigned int num, int node)
250 {
251 	unsigned int i;
252 	int nodes[num];
253 
254 	for (i = 0; i < num; i++) {
255 		nodes[i] = node;
256 	}
257 
258 	verify_pages_on_nodes(pages, status, num, nodes);
259 }
260 
261 /*
262  * alloc_shared_pages_on_node() - allocate shared pages on a NUMA node
263  * @pages: array to store the allocated pages
264  * @num: the no. of pages to allocate
265  * @node: the node in which the pages should be allocated
266  *
267  * RETURNS:
268  * 0 on success, -1 on allocation failure
269  */
alloc_shared_pages_on_node(void ** pages,unsigned int num,int node)270 int alloc_shared_pages_on_node(void **pages, unsigned int num, int node)
271 {
272 #ifdef HAVE_NUMA_V2
273 	char *shared;
274 	unsigned int i;
275 	int nodes[num];
276 	size_t total_size = num * get_page_size();
277 
278 	shared = mmap(NULL, total_size,
279 		      PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
280 	if (shared == MAP_FAILED) {
281 		tst_resm(TBROK | TERRNO, "allocation of shared pages failed");
282 		return -1;
283 	}
284 
285 	numa_tonode_memory(shared, total_size, node);
286 
287 	for (i = 0; i < num; i++) {
288 		char *page;
289 
290 		pages[i] = shared;
291 		shared += get_page_size();
292 
293 		nodes[i] = node;
294 
295 		/* Touch the page to force allocation */
296 		page = pages[i];
297 		page[0] = i;
298 	}
299 
300 	return 0;
301 #else
302 	return -1;
303 #endif
304 }
305 
306 /*
307  * free_shared_pages() - free shared pages
308  * @pages: array of pages to be freed
309  * @num: the no. of pages to free
310  */
free_shared_pages(void ** pages,unsigned int num)311 void free_shared_pages(void **pages, unsigned int num)
312 {
313 	int ret;
314 
315 	ret = munmap(pages[0], num * get_page_size());
316 	if (ret == -1)
317 		tst_resm(TWARN | TERRNO, "unmapping of shared pages failed");
318 }
319 
320 /*
321  * alloc_sem() - allocate semaphores
322  * @num - no. of semaphores to create
323  *
324  * Allocate and initialize semaphores in a shared memory area, so that
325  * the semaphore can be used across processes.
326  *
327  * RETURNS:
328  * Array of initialized semaphores.
329  */
alloc_sem(int num)330 sem_t *alloc_sem(int num)
331 {
332 	sem_t *sem;
333 	void *sem_mem;
334 	int i, ret;
335 
336 	sem_mem = mmap(NULL, get_page_size(),
337 		       PROT_READ | PROT_WRITE,
338 		       MAP_SHARED | MAP_ANONYMOUS, 0, 0);
339 	if (sem_mem == MAP_FAILED) {
340 		tst_resm(TBROK | TERRNO, "allocation of semaphore page failed");
341 		goto err_exit;
342 	}
343 
344 	sem = sem_mem;
345 
346 	for (i = 0; i < num; i++) {
347 		ret = sem_init(&sem[i], 1, 0);
348 		if (ret == -1) {
349 			tst_resm(TBROK | TERRNO, "semaphore initialization "
350 				 "failed");
351 			goto err_free_mem;
352 		}
353 	}
354 
355 	return sem;
356 
357 err_free_mem:
358 	ret = munmap(sem_mem, get_page_size());
359 	if (ret == -1)
360 		tst_resm(TWARN | TERRNO, "error freeing semaphore memory");
361 err_exit:
362 	return NULL;
363 }
364 
365 /*
366  * free_sem() - free semaphores
367  * @sem - array of semphores to be freed
368  * @num - no. of semaphores in the array
369  */
free_sem(sem_t * sem,int num)370 void free_sem(sem_t * sem, int num)
371 {
372 	int i;
373 	int ret;
374 
375 	for (i = 0; i < num; i++) {
376 		ret = sem_destroy(&sem[i]);
377 		if (ret == -1)
378 			tst_resm(TWARN | TERRNO, "error destroying semaphore");
379 	}
380 
381 	ret = munmap(sem, get_page_size());
382 	if (ret == -1)
383 		tst_resm(TWARN | TERRNO, "error freeing semaphore memory");
384 }
385 
386 /*
387  * check_config() - check for required configuration
388  * @min_nodes: the minimum required NUMA nodes
389  *
390  * Checks if numa support is availabe, kernel is >= 2.6.18, arch is
391  * one of the supported architectures.
392  */
check_config(unsigned int min_nodes)393 void check_config(unsigned int min_nodes)
394 {
395 #ifdef HAVE_NUMA_V2
396 	int num_allowed_nodes;
397 	int ret;
398 
399 	ret = get_allowed_nodes_arr(NH_MEMS, &num_allowed_nodes, NULL);
400 	if (ret < 0)
401 		tst_brkm(TBROK | TERRNO, NULL, "get_allowed_nodes(): %d", ret);
402 
403 	if (numa_available() < 0) {
404 		tst_brkm(TCONF, NULL, "NUMA support is not available");
405 	} else if (num_allowed_nodes < min_nodes) {
406 		tst_brkm(TCONF, NULL, "at least %d allowed NUMA nodes"
407 			 " are required", min_nodes);
408 	} else if (tst_kvercmp(2, 6, 18) < 0) {
409 		tst_brkm(TCONF, NULL, "2.6.18 or greater kernel required");
410 	}
411 #else
412 	tst_brkm(TCONF, NULL, NUMA_ERROR_MSG);
413 #endif
414 }
415