From: Konstantin Khorenko <khorenko@virtuozzo.com>
To: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>,
Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
Cc: devel@openvz.org
Subject: [Devel] [PATCH hci-8.1 2/2] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE
Date: Thu, 05 Nov 2026 19:48:45 +0100 [thread overview]
Message-ID: <20261105-b4-prep-khorenko-vstor-153679-mbind-ve-v1-2-b4fcd27c8001@virtuozzo.com> (raw)
In-Reply-To: <20261105-b4-prep-khorenko-vstor-153679-mbind-ve-v1-0-b4fcd27c8001@virtuozzo.com>
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 <khorenko@virtuozzo.com>
---
tools/testing/selftests/ve/.gitignore | 1 +
tools/testing/selftests/ve/Makefile | 1 +
.../ve/ve_numa_syscalls_prohibited_test.c | 192 +++++++++++++++++++++
3 files changed, 194 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..4cae1c74b46bc
--- /dev/null
+++ b/tools/testing/selftests/ve/ve_numa_syscalls_prohibited_test.c
@@ -0,0 +1,192 @@
+// 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;
+
+ 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);
+
+ ASSERT_EQ(syscall(__NR_get_mempolicy, NULL, allowed, NODE_BITS + 1UL,
+ 0UL, MPOL_F_MEMS_ALLOWED), 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
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
next prev parent reply other threads:[~2026-11-05 18:50 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 18:48 [Devel] [PATCH hci-8.1 0/2] mempolicy: prohibit mbind and set_mempolicy_home_node in VE Konstantin Khorenko
2026-11-05 18:48 ` [Devel] [PATCH hci-8.1 1/2] " Konstantin Khorenko
2026-11-05 18:52 ` no-reply
2026-11-05 18:48 ` Konstantin Khorenko [this message]
2026-11-05 18:54 ` [Devel] [PATCH hci-8.1 2/2] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE no-reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261105-b4-prep-khorenko-vstor-153679-mbind-ve-v1-2-b4fcd27c8001@virtuozzo.com \
--to=khorenko@virtuozzo.com \
--cc=devel@openvz.org \
--cc=ptikhomirov@virtuozzo.com \
--cc=vasileios.almpanis@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox