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

Reply via email to