[Devel] [PATCH hci-8.1 v2 4/4] selftests/ve: check that tmpfs rejects mpol= in a VE
Konstantin Khorenko
khorenko at virtuozzo.com
Fri Nov 6 17:01:35 MSK 2026
The test creates a tmpfs superblock with mpol=local and expects it to
succeed on the host and to fail with -EINVAL in a VE. A tmpfs with
size= is created in the VE as well, so the EINVAL can only come from
the mpol= check, not from tmpfs being unavailable in the VE.
https://virtuozzo.atlassian.net/browse/VSTOR-153679
Feature: mm: prohibit NUMA API inside CT
Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
---
tools/testing/selftests/ve/.gitignore | 1 +
tools/testing/selftests/ve/Makefile | 1 +
tools/testing/selftests/ve/ve_tmpfs_mpol_test.c | 123 ++++++++++++++++++++++++
3 files changed, 125 insertions(+)
diff --git a/tools/testing/selftests/ve/.gitignore b/tools/testing/selftests/ve/.gitignore
index 5e07985c8fed3..2ff7669a393aa 100644
--- a/tools/testing/selftests/ve/.gitignore
+++ b/tools/testing/selftests/ve/.gitignore
@@ -5,3 +5,4 @@ ve_cpuid_override_test
ve_vtty_stress
ve_numa_migrate_test
ve_numa_syscalls_prohibited_test
+ve_tmpfs_mpol_test
diff --git a/tools/testing/selftests/ve/Makefile b/tools/testing/selftests/ve/Makefile
index 451a7e1cc17d1..cff4fc2a7639f 100644
--- a/tools/testing/selftests/ve/Makefile
+++ b/tools/testing/selftests/ve/Makefile
@@ -8,6 +8,7 @@ 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
+TEST_GEN_PROGS += ve_tmpfs_mpol_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_tmpfs_mpol_test.c b/tools/testing/selftests/ve/ve_tmpfs_mpol_test.c
new file mode 100644
index 0000000000000..6254f95fadece
--- /dev/null
+++ b/tools/testing/selftests/ve/ve_tmpfs_mpol_test.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+/* The tmpfs mpol= mount option works on the host and is rejected in a VE. */
+#define _GNU_SOURCE
+#include <errno.h>
+#include <sched.h>
+#include <sys/mount.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+#include "ve_selftest.h"
+
+/* Create a tmpfs superblock with one option set: 0 or the errno. */
+static int tmpfs_create(const char *key, const char *val)
+{
+ int fd, err = 0;
+
+ fd = fsopen("tmpfs", FSOPEN_CLOEXEC);
+ if (fd < 0)
+ return errno;
+ if (fsconfig(fd, FSCONFIG_SET_STRING, key, val, 0) < 0 ||
+ fsconfig(fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0)
+ err = errno;
+ close(fd);
+ return err;
+}
+
+struct tmpfs_result {
+ int size_err;
+ int mpol_err;
+};
+
+static void run_tmpfs(struct tmpfs_result *res)
+{
+ res->size_err = tmpfs_create("size", "1M");
+ res->mpol_err = tmpfs_create("mpol", "local");
+}
+
+FIXTURE(tmpfs_mpol)
+{
+ int cgv2_fd;
+ int ctid;
+};
+
+FIXTURE_SETUP(tmpfs_mpol)
+{
+ long ret;
+
+ self->cgv2_fd = -1;
+
+ if (geteuid() != 0)
+ SKIP(return, "creating a ve cgroup needs root");
+
+ ret = syscall(__NR_get_mempolicy, NULL, NULL, 0UL, 0UL, 0UL);
+ if (ret == -1 && errno == ENOSYS)
+ SKIP(return, "no NUMA API on the host");
+ ASSERT_EQ(ret, 0);
+
+ 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(tmpfs_mpol)
+{
+ if (self->cgv2_fd >= 0) {
+ destroy_ve(self->cgv2_fd, self->ctid);
+ close(self->cgv2_fd);
+ }
+}
+
+TEST_F(tmpfs_mpol, host_mpol_accepted)
+{
+ struct tmpfs_result res;
+
+ run_tmpfs(&res);
+ EXPECT_EQ(res.size_err, 0)
+ TH_LOG("tmpfs size= on the host: %s", strerror(res.size_err));
+ EXPECT_EQ(res.mpol_err, 0)
+ TH_LOG("tmpfs mpol= on the host: %s", strerror(res.mpol_err));
+}
+
+TEST_F(tmpfs_mpol, ve_mpol_rejected)
+{
+ struct tmpfs_result res;
+ int pipefd[2], status;
+ 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_tmpfs(&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]);
+
+ EXPECT_EQ(res.size_err, 0)
+ TH_LOG("tmpfs size= in a VE: %s", strerror(res.size_err));
+ EXPECT_EQ(res.mpol_err, EINVAL)
+ TH_LOG("tmpfs mpol= in a VE: %s, expected EINVAL",
+ strerror(res.mpol_err));
+}
+
+TEST_HARNESS_MAIN
--
2.43.0
More information about the Devel
mailing list