blob: 2c59c2acf68c6d105f988f2877816b6e381eb1f8 [file] [log] [blame]
/*
* Copyright (c) 2008 Vijay Kumar B. <vijaykumar@bravegnu.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <sys/mman.h>
#include <syscall.h>
#include <unistd.h>
#include <semaphore.h>
#include "test.h"
#include "move_pages_support.h"
long get_page_size(void)
{
return sysconf(_SC_PAGESIZE);
}
/*
* free_pages() - free an array of pages
* @pages: array of page pointers to be freed
* @num: no. of pages in the array
*/
void free_pages(void **pages, unsigned int num)
{
#if HAVE_NUMA_H
int i;
size_t onepage = get_page_size();
for (i = 0; i < num; i++) {
if (pages[i] != NULL) {
numa_free(pages[i], onepage);
}
}
#endif
}
/*
* alloc_pages_on_nodes() - allocate pages on specified NUMA nodes
* @pages: array in which the page pointers will be stored
* @num: no. of pages to allocate
* @nodes: array of NUMA nodes
*
* A page will be allocated in each node specified by @nodes, and the
* page pointers will be stored in @pages array.
*
* RETURNS:
* 0 on success, -1 on allocation failure.
*/
int alloc_pages_on_nodes(void **pages, unsigned int num, int *nodes)
{
int i;
#if HAVE_NUMA_ALLOC_ONNODE
size_t onepage = get_page_size();
#endif
for (i = 0; i < num; i++) {
pages[i] = NULL;
}
for (i = 0; i < num; i++) {
char *page;
#if HAVE_NUMA_ALLOC_ONNODE
pages[i] = numa_alloc_onnode(onepage, nodes[i]);
#endif
if (pages[i] == NULL) {
tst_resm(TBROK, "allocation of page on node "
"%d failed", nodes[i]);
break;
}
/* Touch the page, to force allocation. */
page = pages[i];
page[0] = i;
}
if (i == num)
return 0;
free_pages(pages, num);
return -1;
}
/*
* alloc_pages_linear() - allocate pages in each NUMA node
* @pages: array in which the page pointers will be stored
* @num: no. of pages to allocate
*
* Pages will be allocated one from each NUMA node, in an interleaved
* fashion.
*
* RETURNS:
* 0 on success, -1 on allocation failure.
*/
int alloc_pages_linear(void **pages, unsigned int num)
{
int nodes[num];
#if HAVE_NUMA_H
unsigned int i;
unsigned int n = 0;
int num_allowed_nodes;
int *allowed_nodes;
int ret;
ret = get_allowed_nodes_arr(NH_MEMS, &num_allowed_nodes,
&allowed_nodes);
if (ret < 0)
tst_brkm(TBROK | TERRNO, NULL, "get_allowed_nodes(): %d", ret);
for (i = 0; i < num; i++) {
nodes[i] = allowed_nodes[n];
n++;
if (n >= num_allowed_nodes)
n = 0;
}
free(allowed_nodes);
#endif
return alloc_pages_on_nodes(pages, num, nodes);
}
/*
* alloc_pages_on_node() - allocate pages on specified NUMA node
* @pages: array in which the page pointers will be stored
* @num: no. of pages to allocate
* @node: NUMA node from which to allocate pages
*
* Pages will be allocated from the NUMA node @node, and the page
* pointers will be stored in the @pages array.
*
* RETURNS:
* 0 on success, -1 on allocation failure.
*/
int alloc_pages_on_node(void **pages, unsigned int num, int node)
{
unsigned int i;
int nodes[num];
for (i = 0; i < num; i++)
nodes[i] = node;
return alloc_pages_on_nodes(pages, num, nodes);
}
/*
* verify_pages_on_nodes() - verify pages are in specified nodes
* @pages: array of pages to be verified
* @status: the NUMA node of each page
* @num: the no. of pages
* @nodes: the expected NUMA nodes
*/
void
verify_pages_on_nodes(void **pages, int *status, unsigned int num, int *nodes)
{
#if HAVE_NUMA_H
unsigned int i;
int which_node;
int ret;
for (i = 0; i < num; i++) {
if (status[i] != nodes[i]) {
tst_resm(TFAIL, "page %d on node %d, "
"expected on node %d", i, status[i], nodes[i]);
return;
}
/* Based on inputs from Andi Kleen.
*
* Retrieves numa node for the given page. This does
* not seem to be documented in the man pages.
*/
ret = get_mempolicy(&which_node, NULL, 0,
pages[i], MPOL_F_NODE | MPOL_F_ADDR);
if (ret == -1) {
tst_resm(TBROK | TERRNO, "error getting memory policy "
"for page %p", pages[i]);
return;
}
if (which_node != nodes[i]) {
tst_resm(TFAIL, "page %p is not in node %d ",
pages[i], nodes[i]);
return;
}
}
tst_resm(TPASS, "pages are present in expected nodes");
#else
tst_resm(TCONF, "NUMA support not provided");
#endif
}
/*
* verify_pages_linear() - verify pages are interleaved
* @pages: array of pages to be verified
* @status: the NUMA node of each page
* @num: the no. of pages
*/
void verify_pages_linear(void **pages, int *status, unsigned int num)
{
#if HAVE_NUMA_H
unsigned int i;
unsigned int n = 0;
int nodes[num];
int num_allowed_nodes;
int *allowed_nodes;
int ret;
ret = get_allowed_nodes_arr(NH_MEMS, &num_allowed_nodes,
&allowed_nodes);
if (ret < 0)
tst_brkm(TBROK | TERRNO, NULL, "get_allowed_nodes(): %d", ret);
for (i = 0; i < num; i++) {
nodes[i] = allowed_nodes[n];
n++;
if (n >= num_allowed_nodes)
n = 0;
}
free(allowed_nodes);
verify_pages_on_nodes(pages, status, num, nodes);
#endif
}
/*
* verify_pages_on_node() - verify pages are in specified node
* @pages: array of pages to be verified
* @status: the NUMA node of each page
* @num: the no. of pages
* @node: the expected NUMA node
*/
void verify_pages_on_node(void **pages, int *status, unsigned int num, int node)
{
unsigned int i;
int nodes[num];
for (i = 0; i < num; i++) {
nodes[i] = node;
}
verify_pages_on_nodes(pages, status, num, nodes);
}
/*
* alloc_shared_pages_on_node() - allocate shared pages on a NUMA node
* @pages: array to store the allocated pages
* @num: the no. of pages to allocate
* @node: the node in which the pages should be allocated
*
* RETURNS:
* 0 on success, -1 on allocation failure
*/
int alloc_shared_pages_on_node(void **pages, unsigned int num, int node)
{
#if HAVE_NUMA_H
char *shared;
unsigned int i;
int nodes[num];
size_t total_size = num * get_page_size();
shared = mmap(NULL, total_size,
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
if (shared == MAP_FAILED) {
tst_resm(TBROK | TERRNO, "allocation of shared pages failed");
return -1;
}
numa_tonode_memory(shared, total_size, node);
for (i = 0; i < num; i++) {
char *page;
pages[i] = shared;
shared += get_page_size();
nodes[i] = node;
/* Touch the page to force allocation */
page = pages[i];
page[0] = i;
}
return 0;
#else
return -1;
#endif
}
/*
* free_shared_pages() - free shared pages
* @pages: array of pages to be freed
* @num: the no. of pages to free
*/
void free_shared_pages(void **pages, unsigned int num)
{
int ret;
ret = munmap(pages[0], num * get_page_size());
if (ret == -1)
tst_resm(TWARN | TERRNO, "unmapping of shared pages failed");
}
/*
* alloc_sem() - allocate semaphores
* @num - no. of semaphores to create
*
* Allocate and initialize semaphores in a shared memory area, so that
* the semaphore can be used accross processes.
*
* RETURNS:
* Array of initialized semaphores.
*/
sem_t *alloc_sem(int num)
{
sem_t *sem;
void *sem_mem;
int i, ret;
sem_mem = mmap(NULL, get_page_size(),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, 0, 0);
if (sem_mem == MAP_FAILED) {
tst_resm(TBROK | TERRNO, "allocation of semaphore page failed");
goto err_exit;
}
sem = sem_mem;
for (i = 0; i < num; i++) {
ret = sem_init(&sem[i], 1, 0);
if (ret == -1) {
tst_resm(TBROK | TERRNO, "semaphore initialization "
"failed");
goto err_free_mem;
}
}
return sem;
err_free_mem:
ret = munmap(sem_mem, get_page_size());
if (ret == -1)
tst_resm(TWARN | TERRNO, "error freeing semaphore memory");
err_exit:
return NULL;
}
/*
* free_sem() - free semaphores
* @sem - array of semphores to be freed
* @num - no. of semaphores in the array
*/
void free_sem(sem_t * sem, int num)
{
int i;
int ret;
for (i = 0; i < num; i++) {
ret = sem_destroy(&sem[i]);
if (ret == -1)
tst_resm(TWARN | TERRNO, "error destroying semaphore");
}
ret = munmap(sem, get_page_size());
if (ret == -1)
tst_resm(TWARN | TERRNO, "error freeing semaphore memory");
}
/*
* check_config() - check for required configuration
* @min_nodes: the minimum required NUMA nodes
*
* Checks if numa support is availabe, kernel is >= 2.6.18, arch is
* one of the supported architectures.
*/
void check_config(unsigned int min_nodes)
{
#if HAVE_NUMA_H && HAVE_NUMAIF_H
int num_allowed_nodes;
int ret;
ret = get_allowed_nodes_arr(NH_MEMS, &num_allowed_nodes, NULL);
if (ret < 0)
tst_brkm(TBROK | TERRNO, NULL, "get_allowed_nodes(): %d", ret);
if (numa_available() < 0) {
tst_brkm(TCONF, NULL, "NUMA support is not available");
} else if (num_allowed_nodes < min_nodes) {
tst_brkm(TCONF, NULL, "at least %d allowed NUMA nodes"
" are required", min_nodes);
} else if (tst_kvercmp(2, 6, 18) < 0) {
tst_brkm(TCONF, NULL, "2.6.18 or greater kernel required");
}
#else
tst_brkm(TCONF, NULL, "NUMA support not provided");
#endif
}