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