On 17.07.2026 01:25, Mirian Shilakadze wrote:
The ve selftests each keep their own copies of the same small pieces: a
private cgroup2 mount, an openat based file write helper, a u64 file
reader, a cgroup join helper, VE cgroup create and destroy, the
CLONE_NEWVE fallback, the ctid search range, and the controller set a
VE cgroup needs enabled. The new ve.proc_permissions test needs them
too, so move them into a shared ve_selftest.h, the helpers made static
inline so they can live in a header, and drop the copies from
ve_ns_owner_test.
It would be really cool to move ve_devcg_bpf and ve_printk tests to
ve directory as well and reuse these nice helpers. Maybe in a separate
task.
No functional change to the tests. The moved code keeps the same
behaviour bar cosmetic reformatting. The shared destroy_ve additionally
retries briefly on a busy cgroup, which only makes teardown more robust.
https://virtuozzo.atlassian.net/browse/VSTOR-135286
Signed-off-by: Mirian Shilakadze <[email protected]>
---
tools/testing/selftests/ve/ve_ns_owner_test.c | 111 +------------
tools/testing/selftests/ve/ve_selftest.h | 151 ++++++++++++++++++
2 files changed, 158 insertions(+), 104 deletions(-)
create mode 100644 tools/testing/selftests/ve/ve_selftest.h
diff --git a/tools/testing/selftests/ve/ve_ns_owner_test.c
b/tools/testing/selftests/ve/ve_ns_owner_test.c
index b5b4f10be72b..82c31dff4b1b 100644
--- a/tools/testing/selftests/ve/ve_ns_owner_test.c
+++ b/tools/testing/selftests/ve/ve_ns_owner_test.c
@@ -43,13 +43,7 @@
#include <errno.h>
#include "../kselftest_harness.h"
-
-#define CTID_MIN 108
-#define CTID_MAX 200
-
-#ifndef CLONE_NEWVE
-#define CLONE_NEWVE 0x00000040
-#endif
+#include "ve_selftest.h"
/*
* Make ve.netns_avail_nr movements easy to detect: a small cap means
@@ -57,81 +51,6 @@
*/
#define VE_NETNS_MAX 3
-static int write_file_at(int dirfd, const char *path, const char *val)
-{
- int fd, ret;
- size_t len = strlen(val);
-
- fd = openat(dirfd, path, O_WRONLY);
- if (fd < 0)
- return -1;
-
- ret = write(fd, val, len);
- close(fd);
- return (ret == (int)len) ? 0 : -1;
-}
-
-static int read_u64_at(int dirfd, const char *path, unsigned long long *out)
-{
- char buf[32] = {0};
- int fd, ret;
-
- fd = openat(dirfd, path, O_RDONLY);
- if (fd < 0)
- return -1;
-
- ret = read(fd, buf, sizeof(buf) - 1);
- close(fd);
- if (ret <= 0)
- return -1;
-
- *out = strtoull(buf, NULL, 10);
- return 0;
-}
-
-static int mount_cg2_fd(void)
-{
- int fs_fd, mnt_fd;
-
- fs_fd = syscall(__NR_fsopen, "cgroup2", 0);
- if (fs_fd < 0)
- return -1;
-
- if (syscall(__NR_fsconfig, fs_fd, FSCONFIG_CMD_CREATE,
- NULL, NULL, 0) < 0) {
- close(fs_fd);
- return -1;
- }
-
- mnt_fd = syscall(__NR_fsmount, fs_fd, 0, 0);
- close(fs_fd);
- return mnt_fd;
-}
-
-static int enter_cgroup(int cgv2_fd, int ctid)
-{
- char cg_path[64];
- char pid_str[64];
- int fd;
- int ret;
-
- if (ctid)
- snprintf(cg_path, sizeof(cg_path), "%d/cgroup.procs", ctid);
- else
- snprintf(cg_path, sizeof(cg_path), "cgroup.procs");
- fd = openat(cgv2_fd, cg_path, O_WRONLY);
- if (fd < 0)
- return -1;
-
- snprintf(pid_str, sizeof(pid_str), "%d", getpid());
- ret = write(fd, pid_str, strlen(pid_str));
- if (ret < 0 || ret != (int)strlen(pid_str))
- ret = -1;
-
- close(fd);
- return ret;
-}
-
/*
* Synchronisation across the clone() boundary: child does its setup,
* tells parent it is ready, then blocks until parent acknowledges.
@@ -199,7 +118,7 @@ static void check_new_ve_owner(struct __test_metadata
*_metadata,
int cgv2_fd, int ctid)
{
unsigned long long avail, mnt;
- char path[64];
+ char path[PATH_MAX];
snprintf(path, sizeof(path), "%d/ve.netns_avail_nr", ctid);
ASSERT_EQ(read_u64_at(cgv2_fd, path, &avail), 0);
@@ -219,15 +138,14 @@ FIXTURE(ve_ns_owner)
FIXTURE_SETUP(ve_ns_owner)
{
unsigned long long initial_mnt_nr;
- char ctid_str[16];
char val[16];
- char path[64];
+ char path[PATH_MAX];
self->cgv2_fd = mount_cg2_fd();
ASSERT_GE(self->cgv2_fd, 0);
ASSERT_EQ(write_file_at(self->cgv2_fd, "cgroup.subtree_control",
- "+cpuset +cpu +cpuacct +io +memory +hugetlb +pids +rdma +misc
+ve"), 0);
+ VE_CONTROLLERS), 0);
ASSERT_EQ(write_file_at(self->cgv2_fd,
"ve.default_sysfs_permissions", "/ rx"), 0);
@@ -236,20 +154,8 @@ FIXTURE_SETUP(ve_ns_owner)
ASSERT_EQ(write_file_at(self->cgv2_fd,
"ve.default_sysfs_permissions", "fs/cgroup rw"), 0);
- self->ctid = CTID_MIN;
- while (self->ctid < CTID_MAX) {
- snprintf(ctid_str, sizeof(ctid_str), "%d", self->ctid);
- if (faccessat(self->cgv2_fd, ctid_str, F_OK, 0) != 0 &&
- errno == ENOENT)
- break;
- self->ctid++;
- }
- ASSERT_LT(self->ctid, CTID_MAX);
-
- ASSERT_EQ(mkdirat(self->cgv2_fd, ctid_str, 0755), 0);
-
- snprintf(path, sizeof(path), "%d/cgroup.controllers_hidden",
self->ctid);
- ASSERT_EQ(write_file_at(self->cgv2_fd, path, "-ve"), 0);
+ self->ctid = make_ve(self->cgv2_fd, CTID_MIN);
+ ASSERT_GE(self->ctid, 0);
/*
* ve.veid and ve.features are deliberately not configured: the
@@ -279,11 +185,8 @@ FIXTURE_SETUP(ve_ns_owner)
FIXTURE_TEARDOWN(ve_ns_owner)
{
- char path[64];
-
enter_cgroup(self->cgv2_fd, 0);
- snprintf(path, sizeof(path), "%d", self->ctid);
- unlinkat(self->cgv2_fd, path, AT_REMOVEDIR);
+ destroy_ve(self->cgv2_fd, self->ctid);
close(self->cgv2_fd);
}
diff --git a/tools/testing/selftests/ve/ve_selftest.h b/tools/testing/selftests/ve/ve_selftest.h
new file mode 100644
index 000000000000..dddb09e948f9
--- /dev/null
+++ b/tools/testing/selftests/ve/ve_selftest.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Shared helpers for the ve selftests: a private cgroup2 mount, small file and
+ * cgroup helpers, and VE cgroup create and destroy, used across the tests in
+ * this directory.
+ */
+#ifndef __SELFTESTS_VE_VE_SELFTEST_H
+#define __SELFTESTS_VE_VE_SELFTEST_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/mount.h>
+#include <linux/mount.h>
+
+#ifndef CLONE_NEWVE
+#define CLONE_NEWVE 0x00000040
+#endif
+
+/* VE cgroup id search range shared by the ve tests. */
+#define CTID_MIN 108
+#define CTID_MAX 200
+
+/* Controllers a VE cgroup needs enabled in the root subtree_control. */
+#define VE_CONTROLLERS \
+ "+cpuset +cpu +cpuacct +io +memory +hugetlb +pids +rdma +misc +ve"
+
+static inline int write_file_at(int dirfd, const char *path, const char *val)
+{
+ int fd, ret;
+ size_t len = strlen(val);
+
+ fd = openat(dirfd, path, O_WRONLY);
+ if (fd < 0)
+ return -1;
+
+ ret = write(fd, val, len);
+ close(fd);
+ return (ret == (int)len) ? 0 : -1;
+}
+
+static inline int read_u64_at(int dirfd, const char *path,
+ unsigned long long *out)
+{
+ char buf[32] = {0};
+ int fd, ret;
+
+ fd = openat(dirfd, path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret <= 0)
+ return -1;
+
+ *out = strtoull(buf, NULL, 10);
Let's verify that number was actually read: check errno,
verify end pointer.
+ return 0;
+}
+
+/* A fresh, private cgroup2 mount, returned as an fd to use with *at() calls.
*/
+static inline int mount_cg2_fd(void)
+{
+ int fs_fd, mnt_fd;
+
+ fs_fd = syscall(__NR_fsopen, "cgroup2", 0);
This function is the only one using syscalls, others use
libc helpers. Maybe use them here as well?
+ if (fs_fd < 0)
+ return -1;
+
+ if (syscall(__NR_fsconfig, fs_fd, FSCONFIG_CMD_CREATE,
+ NULL, NULL, 0) < 0) {
+ close(fs_fd);
+ return -1;
+ }
+
+ mnt_fd = syscall(__NR_fsmount, fs_fd, 0, 0);
+ close(fs_fd);
+ return mnt_fd;
+}
+
+/* Move the caller into cgroup @ctid (or the root when @ctid is 0). */
+static inline int enter_cgroup(int cgv2_fd, int ctid)
+{
+ char cg_path[PATH_MAX];
+ char pid_str[64];
+ int fd, ret;
+
+ if (ctid)
+ snprintf(cg_path, sizeof(cg_path), "%d/cgroup.procs", ctid);
+ else
+ snprintf(cg_path, sizeof(cg_path), "cgroup.procs");
+ fd = openat(cgv2_fd, cg_path, O_WRONLY);
+ if (fd < 0)
+ return -1;
+
+ snprintf(pid_str, sizeof(pid_str), "%d", getpid());
+ ret = write(fd, pid_str, strlen(pid_str));
+ if (ret < 0 || ret != (int)strlen(pid_str))
+ ret = -1;
+
+ close(fd);
+ return ret;
+}
+
+/*
+ * Create a fresh VE cgroup at the first free id at or after @from and unhide
+ * its ve.* control files. Return the new id, or -1.
+ */
+static inline int make_ve(int cgv2_fd, int from)
+{
+ char name[16], path[PATH_MAX];
+ int id;
+
+ for (id = from; id < CTID_MAX; id++) {
+ snprintf(name, sizeof(name), "%d", id);
+ if (faccessat(cgv2_fd, name, F_OK, 0) != 0 && errno == ENOENT)
+ break;
+ }
+ if (id >= CTID_MAX)
+ return -1;
+ if (mkdirat(cgv2_fd, name, 0755) != 0)
+ return -1;
+ snprintf(path, sizeof(path), "%d/cgroup.controllers_hidden", id);
+ if (write_file_at(cgv2_fd, path, "-ve") < 0)
Missing cleanup.
+ return -1;
+ return id;
+}
+
+/* Remove a VE cgroup, retrying briefly while it is still busy. */
+static inline void destroy_ve(int cgv2_fd, int id)
+{
+ char name[16];
+ int i;
+
+ if (id < 0)
+ return;
+ snprintf(name, sizeof(name), "%d", id);
+ for (i = 0; i < 100; i++) {
+ if (unlinkat(cgv2_fd, name, AT_REMOVEDIR) == 0 || errno !=
EBUSY)
+ break;
+ usleep(10000);
+ }
Let's report here that destroy failed - it usually is a problem.
+}
+
+#endif /* __SELFTESTS_VE_VE_SELFTEST_H */
--
Best regards, Riabchun Vladimir
Linux Kernel Developer, Virtuozzo
_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel