From: Jan S. <jst...@re...> - 2012-10-18 12:56:47
|
Use migrate_pages() syscall and check that shared/non-shared memory is migrated to desired node. Signed-off-by: Jan Stancek <jst...@re...> --- runtest/syscalls | 1 + .../syscalls/migrate_pages/migrate_pages02.c | 363 ++++++++++++++++++++ 2 files changed, 364 insertions(+), 0 deletions(-) create mode 100644 testcases/kernel/syscalls/migrate_pages/migrate_pages02.c diff --git a/runtest/syscalls b/runtest/syscalls index 9daf234..78f3bd3 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -518,6 +518,7 @@ memcmp01 memcmp01 memcpy01 memcpy01 migrate_pages01 migrate_pages01 +migrate_pages02 migrate_pages02 mlockall01 mlockall01 mlockall02 mlockall02 diff --git a/testcases/kernel/syscalls/migrate_pages/migrate_pages02.c b/testcases/kernel/syscalls/migrate_pages/migrate_pages02.c new file mode 100644 index 0000000..840aa2b --- /dev/null +++ b/testcases/kernel/syscalls/migrate_pages/migrate_pages02.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2012 Linux Test Project, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it + * is free of the rightful claim of any third person regarding + * infringement or the like. Any license provided herein, whether + * implied or otherwise, applies only to this software file. Patent + * licenses, if any, provided herein do not apply to combinations of + * this program with other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* + * use migrate_pages() and check that address is on correct node + * 1. process A can migrate its non-shared mem with CAP_SYS_NICE + * 2. process A can migrate its non-shared mem without CAP_SYS_NICE + * 3. process A can migrate shared mem only with CAP_SYS_NICE + * 4. process A can migrate non-shared mem in process B with same effective uid + * 5. process A can migrate non-shared mem in process B with CAP_SYS_NICE + */ +#include <sys/types.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <errno.h> +#if HAVE_NUMA_H +#include <numa.h> +#endif +#if HAVE_NUMAIF_H +#include <numaif.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <pwd.h> +#include "config.h" +#include "test.h" +#include "usctest.h" +#include "safe_macros.h" +#include "linux_syscall_numbers.h" +#include "numa_helper.h" +#include "migrate_pages_common.h" + +#define NODE_MIN_FREEMEM 32*1024*1024 + +char *TCID = "migrate_pages02"; +int TST_TOTAL = 1; + +#if defined(__NR_migrate_pages) && HAVE_NUMA_H && HAVE_NUMAIF_H +static char nobody_uid[] = "nobody"; +static struct passwd *ltpuser; +static int *nodes, nodeA, nodeB; +static int num_nodes; + +static void setup(void); +static void cleanup(void); + +option_t options[] = { + { NULL, NULL, NULL } +}; + +static int migrate_to_node(int pid, int node) +{ + unsigned long nodemask_size, max_node; + unsigned long *old_nodes, *new_nodes; + int i; + + tst_resm(TPASS, "pid(%d) migrate pid %d to node -> %d", + getpid(), pid, node); + max_node = get_max_node(); + nodemask_size = max_node/8+1; + old_nodes = SAFE_MALLOC(NULL, nodemask_size); + new_nodes = SAFE_MALLOC(NULL, nodemask_size); + + memset(old_nodes, 0, nodemask_size); + memset(new_nodes, 0, nodemask_size); + for (i = 0; i < num_nodes; i++) + set_bit(old_nodes, nodes[i], 1); + set_bit(new_nodes, node, 1); + + TEST(syscall(__NR_migrate_pages, pid, max_node, old_nodes, new_nodes)); + if (TEST_RETURN == -1) + tst_resm(TFAIL|TERRNO, "migrate_pages failed "); + return TEST_RETURN; +} + +static int addr_on_node(void *addr) +{ + int node; + int ret; + + ret = syscall(__NR_get_mempolicy, &node, NULL, (unsigned long)0, + (unsigned long) addr, MPOL_F_NODE | MPOL_F_ADDR); + if (ret == -1) { + tst_resm(TBROK | TERRNO, "error getting memory policy " + "for page %p", addr); + } + return node; +} + +static int check_addr_on_node(void *addr, int exp_node) +{ + int node; + + node = addr_on_node(addr); + if (node == exp_node) { + tst_resm(TPASS, "pid(%d) addr %p is on expected node: %d", + getpid(), addr, exp_node); + return 0; + } else { + tst_resm(TFAIL, "pid(%d) addr %p not on expected node: %d " + ", expected %d", getpid(), addr, node, + exp_node); + return 1; + } +} + +static void test_migrate_current_process(int node1, int node2, + int cap_sys_nice) +{ + char *testp, *testp2; + int ret, status; + pid_t child; + + /* parent can migrate its non-shared memory */ + tst_resm(TINFO, "current_process, cap_sys_nice: %d", cap_sys_nice); + testp = SAFE_MALLOC(NULL, getpagesize()); + testp[0] = 0; + tst_resm(TINFO, "private anonymous: %p", testp); + migrate_to_node(0, node2); + check_addr_on_node(testp, node2); + migrate_to_node(0, node1); + check_addr_on_node(testp, node1); + free(testp); + + /* parent can migrate shared memory with CAP_SYS_NICE */ + testp2 = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_SHARED, 0, 0); + if (testp2 == MAP_FAILED) + tst_brkm(TBROK|TERRNO, cleanup, "mmap failed"); + testp2[0] = 1; + tst_resm(TINFO, "shared anonymous: %p", testp2); + migrate_to_node(0, node2); + check_addr_on_node(testp2, node2); + + /* shared mem is on node2, try to migrate in child to node1 */ + fflush(stdout); + child = fork(); + switch (child) { + case -1: + tst_brkm(TBROK|TERRNO, cleanup, "fork"); + break; + case 0: + tst_resm(TINFO, "child shared anonymous, cap_sys_nice: %d", + cap_sys_nice); + testp = SAFE_MALLOC(NULL, getpagesize()); + testp[0] = 1; + testp2[0] = 1; + if (!cap_sys_nice) + if (seteuid(ltpuser->pw_uid) == -1) + tst_brkm(TBROK|TERRNO, NULL, "seteuid failed"); + + migrate_to_node(0, node1); + /* child can migrate non-shared memory */ + ret = check_addr_on_node(testp, node1); + + free(testp); + munmap(testp2, getpagesize()); + exit(ret); + default: + if (waitpid(child, &status, 0) == -1) + tst_brkm(TBROK|TERRNO, cleanup, "waitpid"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + tst_resm(TFAIL, "child returns %d", status); + if (cap_sys_nice) + /* child can migrate shared memory only + * with CAP_SYS_NICE */ + check_addr_on_node(testp2, node1); + else + check_addr_on_node(testp2, node2); + munmap(testp2, getpagesize()); + } +} + +static void test_migrate_other_process(int node1, int node2, + int cap_sys_nice) +{ + char *testp; + int status, ret, tmp; + pid_t child; + int child_ready[2]; + int pages_migrated[2]; + + /* setup pipes to synchronize child/parent */ + if (pipe(child_ready) == -1) + tst_resm(TBROK | TERRNO, "pipe #1 failed"); + if (pipe(pages_migrated) == -1) + tst_resm(TBROK | TERRNO, "pipe #2 failed"); + + tst_resm(TINFO, "other_process, cap_sys_nice: %d", cap_sys_nice); + + fflush(stdout); + child = fork(); + switch (child) { + case -1: + tst_brkm(TBROK|TERRNO, cleanup, "fork"); + break; + case 0: + close(child_ready[0]); + close(pages_migrated[1]); + + testp = SAFE_MALLOC(NULL, getpagesize()); + testp[0] = 0; + + /* make sure we are on node1 */ + migrate_to_node(0, node1); + check_addr_on_node(testp, node1); + + if (seteuid(ltpuser->pw_uid) == -1) + tst_brkm(TBROK|TERRNO, NULL, "seteuid failed"); + + /* signal parent it's OK to migrate child and wait */ + if (write(child_ready[1], &tmp, 1) != 1) + tst_brkm(TBROK|TERRNO, NULL, "write #1 failed"); + if (read(pages_migrated[0], &tmp, 1) != 1) + tst_brkm(TBROK|TERRNO, NULL, "read #1 failed"); + + /* parent can migrate child process with same euid */ + /* parent can migrate child process with CAP_SYS_NICE */ + ret = check_addr_on_node(testp, node2); + + free(testp); + close(child_ready[1]); + close(pages_migrated[0]); + exit(ret); + default: + close(child_ready[1]); + close(pages_migrated[0]); + + if (!cap_sys_nice) + if (seteuid(ltpuser->pw_uid) == -1) + tst_brkm(TBROK|TERRNO, NULL, "seteuid failed"); + + /* wait until child is ready on node1, then migrate and + * signal to check current node */ + if (read(child_ready[0], &tmp, 1) != 1) + tst_brkm(TBROK|TERRNO, NULL, "read #2 failed"); + migrate_to_node(child, node2); + if (write(pages_migrated[1], &tmp, 1) != 1) + tst_brkm(TBROK|TERRNO, NULL, "write #2 failed"); + + if (waitpid(child, &status, 0) == -1) + tst_brkm(TBROK|TERRNO, cleanup, "waitpid"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + tst_resm(TFAIL, "child returns %d", status); + close(child_ready[0]); + close(pages_migrated[1]); + + /* reset euid, so this testcase can be used in loop */ + if (!cap_sys_nice) + if (seteuid(0) == -1) + tst_brkm(TBROK|TERRNO, NULL, "seteuid failed"); + } +} + +int main(int argc, char *argv[]) +{ + int lc; + char *msg; + + msg = parse_opts(argc, argv, options, NULL); + if (msg != NULL) + tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); + + setup(); + for (lc = 0; TEST_LOOPING(lc); lc++) { + Tst_count = 0; + test_migrate_current_process(nodeA, nodeB, 1); + test_migrate_current_process(nodeA, nodeB, 0); + test_migrate_other_process(nodeA, nodeB, 1); + test_migrate_other_process(nodeA, nodeB, 0); + } + cleanup(); + tst_exit(); +} + +static void setup(void) +{ + int ret, i; + long long freep, maxA, maxB, node_size; + + tst_require_root(NULL); + TEST(syscall(__NR_migrate_pages, 0, 0, NULL, NULL)); + + if (numa_available() == -1) + tst_brkm(TCONF, NULL, "NUMA not available"); + + ret = get_allowed_nodes_arr(NH_MEMS, &num_nodes, &nodes); + if (ret < 0) + tst_brkm(TBROK|TERRNO, NULL, "get_allowed_nodes(): %d", ret); + + if (num_nodes < 2) + tst_brkm(TCONF, NULL, "at least 2 allowed NUMA nodes" + " are required"); + else if (tst_kvercmp(2, 6, 18) < 0) + tst_brkm(TCONF, NULL, "2.6.18 or greater kernel required"); + + /* get 2 nodes with max free mem */ + maxA = maxB = 0; + nodeA = nodeB = -1; + for (i=0; i<num_nodes; i++) { + node_size = numa_node_size64(nodes[i], &freep); + if (node_size < 0) + tst_brkm(TBROK|TERRNO, NULL, "numa_node_size64 failed"); + if (freep > NODE_MIN_FREEMEM) { + if (freep > maxA) { + maxB = maxA; + nodeB = nodeA; + maxA = freep; + nodeA = nodes[i]; + } else if (freep > maxB) { + maxB = freep; + nodeB = nodes[i]; + } + } + } + + if (nodeA == -1 || nodeB == -1) + tst_brkm(TCONF, NULL, "at least 2 NUMA nodes with free mem > %d are needed", NODE_MIN_FREEMEM); + tst_resm(TINFO, "Using nodes: %d %d", nodeA, nodeB); + + ltpuser = getpwnam(nobody_uid); + if (ltpuser == NULL) + tst_brkm(TBROK|TERRNO, NULL, "getpwnam failed"); + + TEST_PAUSE; +} + +static void cleanup(void) +{ + free(nodes); + TEST_CLEANUP; +} + +#else /* __NR_migrate_pages */ +int main(void) +{ + tst_brkm(TCONF, NULL, "System doesn't support __NR_migrate_pages" + " or libnuma is not available"); +} +#endif -- 1.7.1 |