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 <[email protected]>
---
 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

_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to