A CT must see a kernel without NUMA, so all six NUMA API syscalls return -ENOSYS in a non-super VE.
The test makes each call with arguments that succeed on the host and checks that the host gets 0, while a task in a VE gets -ENOSYS. The host result shows that the ENOSYS can only come from the VE check, not from a bad argument. https://virtuozzo.atlassian.net/browse/VSTOR-153679 Feature: mm: prohibit NUMA API inside CT Signed-off-by: Konstantin Khorenko <[email protected]> --- tools/testing/selftests/ve/.gitignore | 1 + tools/testing/selftests/ve/Makefile | 1 + .../ve/ve_numa_syscalls_prohibited_test.c | 196 +++++++++++++++++++++ 3 files changed, 198 insertions(+) diff --git a/tools/testing/selftests/ve/.gitignore b/tools/testing/selftests/ve/.gitignore index 3d1179bd2c5f4..5e07985c8fed3 100644 --- a/tools/testing/selftests/ve/.gitignore +++ b/tools/testing/selftests/ve/.gitignore @@ -4,3 +4,4 @@ ve_mount_accounting_test ve_cpuid_override_test ve_vtty_stress ve_numa_migrate_test +ve_numa_syscalls_prohibited_test diff --git a/tools/testing/selftests/ve/Makefile b/tools/testing/selftests/ve/Makefile index e96320a183844..451a7e1cc17d1 100644 --- a/tools/testing/selftests/ve/Makefile +++ b/tools/testing/selftests/ve/Makefile @@ -7,6 +7,7 @@ TEST_GEN_PROGS += ve_perms_test TEST_GEN_PROGS += ve_mount_accounting_test TEST_GEN_PROGS += ve_vtty_stress TEST_GEN_PROGS += ve_numa_migrate_test +TEST_GEN_PROGS += ve_numa_syscalls_prohibited_test # CPUID faulting is x86-64 only. ARCH says "x86" for 32-bit too and lib.mk # picks the clang target by its emptiness, so decide by the compiler. diff --git a/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c b/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c new file mode 100644 index 0000000000000..c76fcb1c79ff7 --- /dev/null +++ b/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Every NUMA API syscall works on the host and returns ENOSYS in a VE. */ +#define _GNU_SOURCE +#include <errno.h> +#include <sched.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <unistd.h> +#include <linux/mempolicy.h> + +#include "../kselftest_harness.h" +#include "ve_selftest.h" + +#define NODE_BITS 1024 +#define LONG_BITS (8 * sizeof(unsigned long)) + +static unsigned long node_mask[NODE_BITS / LONG_BITS]; +static void *page; +static size_t page_size; + +static long call_get_mempolicy(void) +{ + int mode; + + return syscall(__NR_get_mempolicy, &mode, NULL, 0UL, 0UL, 0UL); +} + +static long call_set_mempolicy(void) +{ + return syscall(__NR_set_mempolicy, MPOL_DEFAULT, NULL, 0UL); +} + +static long call_mbind(void) +{ + return syscall(__NR_mbind, page, page_size, MPOL_BIND, node_mask, + NODE_BITS + 1UL, 0U); +} + +static long call_set_mempolicy_home_node(void) +{ + unsigned long node; + + for (node = 0; node < NODE_BITS; node++) + if (node_mask[node / LONG_BITS] & (1UL << (node % LONG_BITS))) + break; + return syscall(__NR_set_mempolicy_home_node, page, page_size, node, 0UL); +} + +static long call_migrate_pages(void) +{ + unsigned long none[NODE_BITS / LONG_BITS] = { 0 }; + + return syscall(__NR_migrate_pages, 0, NODE_BITS + 1UL, none, node_mask); +} + +static long call_move_pages(void) +{ + return syscall(__NR_move_pages, 0, 0UL, NULL, NULL, NULL, 0); +} + +/* set_mempolicy_home_node() needs the MPOL_BIND policy mbind() installs. */ +static const struct numa_call { + const char *name; + long (*call)(void); +} numa_calls[] = { + { "get_mempolicy", call_get_mempolicy }, + { "set_mempolicy", call_set_mempolicy }, + { "mbind", call_mbind }, + { "set_mempolicy_home_node", call_set_mempolicy_home_node }, + { "migrate_pages", call_migrate_pages }, + { "move_pages", call_move_pages }, +}; + +#define NR_CALLS ARRAY_SIZE(numa_calls) + +struct call_result { + long ret; + int err; +}; + +static void run_calls(struct call_result *res) +{ + unsigned int i; + + for (i = 0; i < NR_CALLS; i++) { + errno = 0; + res[i].ret = numa_calls[i].call(); + res[i].err = errno; + } +} + +FIXTURE(numa_syscalls) +{ + int cgv2_fd; + int ctid; +}; + +FIXTURE_SETUP(numa_syscalls) +{ + unsigned long allowed[NODE_BITS / LONG_BITS] = { 0 }; + unsigned int i; + long ret; + + self->cgv2_fd = -1; + + if (geteuid() != 0) + SKIP(return, "creating a ve cgroup needs root"); + + page_size = getpagesize(); + page = mmap(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(page, MAP_FAILED); + + ret = syscall(__NR_get_mempolicy, NULL, allowed, NODE_BITS + 1UL, 0UL, + MPOL_F_MEMS_ALLOWED); + if (ret == -1 && errno == ENOSYS) + SKIP(return, "no NUMA API on the host"); + ASSERT_EQ(ret, 0); + for (i = 0; i < NODE_BITS; i++) { + if (allowed[i / LONG_BITS] & (1UL << (i % LONG_BITS))) { + node_mask[i / LONG_BITS] = 1UL << (i % LONG_BITS); + break; + } + } + ASSERT_LT(i, NODE_BITS); + + self->cgv2_fd = mount_cg2_fd(); + ASSERT_GE(self->cgv2_fd, 0); + ASSERT_EQ(write_file_at(self->cgv2_fd, "cgroup.subtree_control", + VE_CONTROLLERS), 0); + + self->ctid = make_ve(self->cgv2_fd, CTID_MIN); + ASSERT_GE(self->ctid, 0); +} + +FIXTURE_TEARDOWN(numa_syscalls) +{ + if (self->cgv2_fd >= 0) { + destroy_ve(self->cgv2_fd, self->ctid); + close(self->cgv2_fd); + } +} + +TEST_F(numa_syscalls, host_calls_succeed) +{ + struct call_result res[NR_CALLS]; + unsigned int i; + + run_calls(res); + for (i = 0; i < NR_CALLS; i++) { + EXPECT_EQ(res[i].ret, 0) + TH_LOG("%s on the host: %s", numa_calls[i].name, + strerror(res[i].err)); + } +} + +TEST_F(numa_syscalls, ve_calls_return_enosys) +{ + struct call_result res[NR_CALLS]; + int pipefd[2], status; + unsigned int i; + pid_t pid; + + ASSERT_EQ(pipe(pipefd), 0); + pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) { + close(pipefd[0]); + if (enter_cgroup(self->cgv2_fd, self->ctid) < 0) + _exit(1); + if (unshare(CLONE_NEWVE) < 0) + _exit(2); + run_calls(res); + if (write(pipefd[1], res, sizeof(res)) != sizeof(res)) + _exit(3); + _exit(0); + } + close(pipefd[1]); + ASSERT_EQ(waitpid(pid, &status, 0), pid); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), 0) + TH_LOG("VE child setup failed at step %d", WEXITSTATUS(status)); + ASSERT_EQ(read(pipefd[0], res, sizeof(res)), sizeof(res)); + close(pipefd[0]); + + for (i = 0; i < NR_CALLS; i++) { + EXPECT_TRUE(res[i].ret == -1 && res[i].err == ENOSYS) + TH_LOG("%s in a VE: returned %ld (%s), expected ENOSYS", + numa_calls[i].name, res[i].ret, + strerror(res[i].err)); + } +} + +TEST_HARNESS_MAIN -- 2.43.0 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
