1 /* 2 * Copyright (c) 2008 Vijay Kumar B. <vijaykumar (at) 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 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 */ 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 */ 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 < num; i++) { 71 pages[i] = NULL; 72 } 73 74 for (i = 0; i < 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 == 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 accross processes. 326 * 327 * RETURNS: 328 * Array of initialized semaphores. 329 */ 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 */ 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 */ 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 support not provided"); 413 #endif 414 } 415