/* * Copyright (c) Crackerjack Project., 2007-2008, Hitachi, Ltd * Copyright (c) 2017 Petr Vorel * * Authors: * Takahiro Yasui , * Yumiko Sugita , * Satoshi Fujiwara * * 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 would 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, see . */ #include #if HAVE_NUMA_H #include #endif #include "config.h" #include "numa_helper.h" #include "tst_test.h" #ifdef HAVE_NUMA_V2 #define MEM_LENGTH (4 * 1024 * 1024) #define UNKNOWN_POLICY -1 #define POLICY_DESC(x) .policy = x, .desc = #x #define POLICY_DESC_TEXT(x, y) .policy = x, .desc = #x" ("y")" static struct bitmask *nodemask, *getnodemask, *empty_nodemask; static void test_default(unsigned int i, char *p); static void test_none(unsigned int i, char *p); static void test_invalid_nodemask(unsigned int i, char *p); struct test_case { int policy; const char *desc; unsigned flags; int ret; int err; void (*test)(unsigned int, char *); struct bitmask **exp_nodemask; }; static struct test_case tcase[] = { { POLICY_DESC(MPOL_DEFAULT), .ret = 0, .err = 0, .test = test_none, .exp_nodemask = &empty_nodemask, }, { POLICY_DESC_TEXT(MPOL_DEFAULT, "target exists"), .ret = -1, .err = EINVAL, .test = test_default, }, { POLICY_DESC_TEXT(MPOL_BIND, "no target"), .ret = -1, .err = EINVAL, .test = test_none, }, { POLICY_DESC(MPOL_BIND), .ret = 0, .err = 0, .test = test_default, .exp_nodemask = &nodemask, }, { POLICY_DESC_TEXT(MPOL_INTERLEAVE, "no target"), .ret = -1, .err = EINVAL, .test = test_none, }, { POLICY_DESC(MPOL_INTERLEAVE), .ret = 0, .err = 0, .test = test_default, .exp_nodemask = &nodemask, }, { POLICY_DESC_TEXT(MPOL_PREFERRED, "no target"), .ret = 0, .err = 0, .test = test_none, }, { POLICY_DESC(MPOL_PREFERRED), .ret = 0, .err = 0, .test = test_default, .exp_nodemask = &nodemask, }, { POLICY_DESC(UNKNOWN_POLICY), .ret = -1, .err = EINVAL, .test = test_none, }, { POLICY_DESC_TEXT(MPOL_DEFAULT, "invalid flags"), .flags = -1, .ret = -1, .err = EINVAL, .test = test_none, }, { POLICY_DESC_TEXT(MPOL_PREFERRED, "invalid nodemask"), .ret = -1, .err = EFAULT, .test = test_invalid_nodemask, }, }; static void test_default(unsigned int i, char *p) { struct test_case *tc = &tcase[i]; TEST(mbind(p, MEM_LENGTH, tc->policy, nodemask->maskp, nodemask->size, tc->flags)); } static void test_none(unsigned int i, char *p) { struct test_case *tc = &tcase[i]; TEST(mbind(p, MEM_LENGTH, tc->policy, NULL, 0, tc->flags)); } static void test_invalid_nodemask(unsigned int i, char *p) { struct test_case *tc = &tcase[i]; /* use invalid nodemask (64 MiB after heap) */ TEST(mbind(p, MEM_LENGTH, tc->policy, sbrk(0) + 64*1024*1024, NUMA_NUM_NODES, tc->flags)); } static void setup(void) { if (!is_numa(NULL, NH_MEMS, 1)) tst_brk(TCONF, "requires NUMA with at least 1 node"); empty_nodemask = numa_allocate_nodemask(); } static void setup_node(void) { int test_node = -1; if (get_allowed_nodes(NH_MEMS, 1, &test_node) < 0) tst_brk(TBROK | TERRNO, "get_allowed_nodes failed"); nodemask = numa_allocate_nodemask(); getnodemask = numa_allocate_nodemask(); numa_bitmask_setbit(nodemask, test_node); } static void do_test(unsigned int i) { struct test_case *tc = &tcase[i]; int policy, fail = 0; char *p = NULL; tst_res(TINFO, "case %s", tc->desc); setup_node(); p = mmap(NULL, MEM_LENGTH, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (p == MAP_FAILED) tst_brk(TBROK | TERRNO, "mmap"); tc->test(i, p); if (TST_RET >= 0) { /* Check policy of the allocated memory */ TEST(get_mempolicy(&policy, getnodemask->maskp, getnodemask->size, p, MPOL_F_ADDR)); if (TST_RET < 0) { tst_res(TFAIL | TTERRNO, "get_mempolicy failed"); return; } if (tc->policy != policy) { tst_res(TFAIL, "Wrong policy: %d, expected: %d", tc->policy, policy); fail = 1; } if (tc->exp_nodemask) { struct bitmask *exp_mask = *(tc->exp_nodemask); if (!numa_bitmask_equal(exp_mask, getnodemask)) { tst_res(TFAIL, "masks are not equal"); tst_res_hexd(TINFO, exp_mask->maskp, exp_mask->size / 8, "exp_mask: "); tst_res_hexd(TINFO, getnodemask->maskp, getnodemask->size / 8, "returned: "); fail = 1; } } } if (TST_RET != tc->ret) { tst_res(TFAIL, "wrong return code: %ld, expected: %d", TST_RET, tc->ret); fail = 1; } if (TST_RET == -1 && TST_ERR != tc->err) { tst_res(TFAIL | TTERRNO, "expected errno: %s, got", tst_strerrno(tc->err)); fail = 1; } if (!fail) tst_res(TPASS, "Test passed"); } static struct tst_test test = { .tcnt = ARRAY_SIZE(tcase), .test = do_test, .setup = setup, }; #else TST_TEST_TCONF(NUMA_ERROR_MSG); #endif