OpenVZ / Virtuozzo kernel development (devel@openvz.org)
 help / color / mirror / Atom feed
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 v2 2/4] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE
Date: Fri, 06 Nov 2026 15:01:33 +0100	[thread overview]
Message-ID: <20261106-b4-prep-khorenko-vstor-153679-mbind-ve-v2-2-6327b23a2d93@virtuozzo.com> (raw)
In-Reply-To: <20261106-b4-prep-khorenko-vstor-153679-mbind-ve-v2-0-6327b23a2d93@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          | 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
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

  parent reply	other threads:[~2026-11-06 14:03 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-06 14:01 [Devel] [PATCH hci-8.1 v2 0/4] mempolicy: prohibit mbind and set_mempolicy_home_node in VE Konstantin Khorenko
2026-11-06 14:01 ` [Devel] [PATCH hci-8.1 v2 1/4] " Konstantin Khorenko
2026-11-06 14:05   ` no-reply
2026-11-06 14:01 ` Konstantin Khorenko [this message]
2026-11-06 14:06   ` [Devel] [PATCH hci-8.1 v2 2/4] selftests/ve: check that the NUMA API syscalls return ENOSYS in a VE no-reply
2026-11-06 14:01 ` [Devel] [PATCH hci-8.1 v2 3/4] shmem: reject the mpol= mount option in VE Konstantin Khorenko
2026-11-06 14:05   ` no-reply
2026-11-06 14:01 ` [Devel] [PATCH hci-8.1 v2 4/4] selftests/ve: check that tmpfs rejects mpol= in a VE Konstantin Khorenko
2026-11-06 14:07   ` no-reply
2026-11-06 14:19 ` [Devel] [PATCH hci-8.1 v2 0/4] mempolicy: prohibit mbind and set_mempolicy_home_node in VE Vasileios Almpanis

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=20261106-b4-prep-khorenko-vstor-153679-mbind-ve-v2-2-6327b23a2d93@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