Home | History | Annotate | Download | only in move_pages
      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