* [Devel] [PATCH VZ10 v3 1/3] selftests/ve: Add namespace management helpers
2026-09-09 21:32 [Devel] [PATCH VZ10 v3 0/3] Improvements for ve selftests Vladimir Riabchun
@ 2026-09-09 21:32 ` Vladimir Riabchun
2026-09-09 21:32 ` [Devel] [PATCH VZ10 v3 2/3] selftests/ve: Add more helpers Vladimir Riabchun
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Vladimir Riabchun @ 2026-09-09 21:32 UTC (permalink / raw)
To: Konstantin Khorenko, vasileios.almpanis; +Cc: devel
When mount namespace is created in a child, it is destroyed
when the child dies. We may need to keep it for a while to check
how VE handles resources.
Instead of keeping a thread which lives in a new ve+mnt namespaces,
let's store open namespace file descriptors to enter them later
without keeping children alive.
https://virtuozzo.atlassian.net/browse/VSTOR-144504
Feature: ve selftests
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
---
tools/testing/selftests/ve/ve_selftest.h | 126 +++++++++++++++++++++++++++++++
1 file changed, 126 insertions(+)
diff --git a/tools/testing/selftests/ve/ve_selftest.h b/tools/testing/selftests/ve/ve_selftest.h
index c53bf7900d20..22c5c8dfc2d1 100644
--- a/tools/testing/selftests/ve/ve_selftest.h
+++ b/tools/testing/selftests/ve/ve_selftest.h
@@ -31,6 +31,132 @@
#define VE_CONTROLLERS \
"+cpuset +cpu +cpuacct +io +memory +hugetlb +pids +rdma +misc +ve"
+struct namespace_ctx {
+ int ns_save_mask; /* Mask of stored CLONE_NEW* namespaces. */
+ /* Namespace file descriptors. Name should match /proc/PID/ns/NAME */
+ int ve_fd;
+ int mnt_fd;
+};
+
+static inline void close_namespace_fds(struct namespace_ctx *ctx)
+{
+ if (ctx->ns_save_mask & CLONE_NEWVE)
+ close(ctx->ve_fd);
+ if (ctx->ns_save_mask & CLONE_NEWNS)
+ close(ctx->mnt_fd);
+
+ ctx->ns_save_mask = 0;
+}
+
+static inline int save_namespace_fds(struct namespace_ctx *ctx, int flags)
+{
+#define OPEN_NS(name, flag) \
+do { \
+ if (flags & (flag)) { \
+ ctx->name##_fd = open("/proc/self/ns/" #name, O_RDONLY); \
+ if (ctx->name##_fd < 0) { \
+ fprintf(stderr, "%s: could not open fd %s: %s\n", \
+ __func__, #name, strerror(errno)); \
+ goto err; \
+ } \
+ ctx->ns_save_mask |= (flag); \
+ } \
+} while (0)
+
+ ctx->ns_save_mask = 0;
+
+ OPEN_NS(ve, CLONE_NEWVE);
+ OPEN_NS(mnt, CLONE_NEWNS);
+
+#undef OPEN_NS
+ return 0;
+
+err:
+ close_namespace_fds(ctx);
+ return -1;
+}
+
+static inline int enter_namespaces(struct namespace_ctx *ctx, int flags)
+{
+#define JOIN_NS(name, flag) \
+do { \
+ if (flags & (flag)) { \
+ if (!(ctx->ns_save_mask & (flag))) { \
+ fprintf(stderr, "%s: could not switch ns %s:" \
+ " no fd in context.\n", __func__, #name); \
+ return -1; \
+ } \
+ if (setns(ctx->name##_fd, (flag))) { \
+ fprintf(stderr, "%s: could not switch ns %s: %s\n", \
+ __func__, #name, strerror(errno)); \
+ return -1; \
+ } \
+ } \
+} while (0)
+
+ JOIN_NS(ve, CLONE_NEWVE);
+ JOIN_NS(mnt, CLONE_NEWNS);
+
+#undef JOIN_NS
+ return 0;
+}
+
+static inline int enter_cgroup(int cgv2_fd, int ctid);
+
+/*
+ * Create new namespaces and store them in ctx.
+ * CLONE_NEWVE is treated specially - before unshare corresponding
+ * CT cgroup is entered, after it root cgroup is entered(not original one).
+ */
+static inline int prepare_namespaces(struct namespace_ctx *ctx, int flags,
+ int cgv2_fd, int ctid)
+{
+ struct namespace_ctx tmp_ctx;
+ int ret = 0;
+
+ if (save_namespace_fds(&tmp_ctx, flags) < 0)
+ return -1;
+
+ if (flags & CLONE_NEWVE) {
+ if (enter_cgroup(cgv2_fd, ctid) < 0) {
+ fprintf(stderr, "%s: failed to enter CT cgroup\n", __func__);
+ ret = -1;
+ goto restore_ns;
+ }
+ }
+
+ if (unshare(flags) < 0) {
+ fprintf(stderr, "%s: failed to unshare, flags %x: %s\n",
+ __func__, flags, strerror(errno));
+ ret = -1;
+ goto restore_cg;
+ }
+
+ if (save_namespace_fds(ctx, flags)) {
+ fprintf(stderr, "%s: failed to save new namespaces, flags %x\n",
+ __func__, flags);
+ ret = -1;
+ }
+
+restore_cg:
+ if (flags & CLONE_NEWVE) {
+ if (enter_cgroup(cgv2_fd, 0) < 0) {
+ fprintf(stderr, "%s: failed to restore root cgroup\n", __func__);
+ ret = -1;
+ }
+ }
+
+restore_ns:
+ if (enter_namespaces(&tmp_ctx, flags) < 0) {
+ fprintf(stderr, "%s: failed to restore old namespaces, flags %x\n",
+ __func__, flags);
+ ret = -1;
+ }
+
+ close_namespace_fds(&tmp_ctx);
+ return ret;
+}
+
static inline int write_file_at(int dirfd, const char *path, const char *val)
{
int fd, ret;
--
2.47.1
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 5+ messages in thread* [Devel] [PATCH VZ10 v3 2/3] selftests/ve: Add more helpers
2026-09-09 21:32 [Devel] [PATCH VZ10 v3 0/3] Improvements for ve selftests Vladimir Riabchun
2026-09-09 21:32 ` [Devel] [PATCH VZ10 v3 1/3] selftests/ve: Add namespace management helpers Vladimir Riabchun
@ 2026-09-09 21:32 ` Vladimir Riabchun
2026-09-09 21:32 ` [Devel] [PATCH VZ10 v3 3/3] ve_namespace: Allow entering non-running VE namespace Vladimir Riabchun
2026-09-15 12:28 ` [Devel] [PATCH VZ10 v3 0/3] Improvements for ve selftests Vasileios Almpanis
3 siblings, 0 replies; 5+ messages in thread
From: Vladimir Riabchun @ 2026-09-09 21:32 UTC (permalink / raw)
To: Konstantin Khorenko, vasileios.almpanis; +Cc: devel
Some more read/write helpers may be useful.
Also, add a helper to execute functions in child process with
switched namespaces and cgroup.
https://virtuozzo.atlassian.net/browse/VSTOR-144504
Feature: ve selftests
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
---
tools/testing/selftests/ve/ve_selftest.h | 85 +++++++++++++++++++++++++++++---
1 file changed, 78 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/ve/ve_selftest.h b/tools/testing/selftests/ve/ve_selftest.h
index 22c5c8dfc2d1..bf9b89b59a6c 100644
--- a/tools/testing/selftests/ve/ve_selftest.h
+++ b/tools/testing/selftests/ve/ve_selftest.h
@@ -171,6 +171,14 @@ static inline int write_file_at(int dirfd, const char *path, const char *val)
return (ret == (int)len) ? 0 : -1;
}
+static inline int write_u64_at(int dirfd, const char *path, unsigned long long val)
+{
+ char s[32];
+
+ snprintf(s, sizeof(s), "%llu", val);
+ return write_file_at(dirfd, path, s);
+}
+
static inline int read_file_at(int dirfd, const char *path, char *buf,
size_t buflen)
{
@@ -201,14 +209,9 @@ static inline int read_u64_at(int dirfd, const char *path,
unsigned long long *out)
{
char buf[32] = {0}, *end;
- int fd, ret;
-
- fd = openat(dirfd, path, O_RDONLY);
- if (fd < 0)
- return -1;
+ int ret;
- ret = read(fd, buf, sizeof(buf) - 1);
- close(fd);
+ ret = read_file_at(dirfd, path, buf, sizeof(buf));
if (ret <= 0)
return -1;
@@ -219,6 +222,25 @@ static inline int read_u64_at(int dirfd, const char *path,
return 0;
}
+static inline int read_s32_at(int dirfd, const char *path,
+ int *out)
+{
+ char buf[32] = {0}, *end;
+ long tmp;
+ int ret;
+
+ ret = read_file_at(dirfd, path, buf, sizeof(buf));
+ if (ret <= 0)
+ return -1;
+
+ errno = 0;
+ tmp = strtol(buf, &end, 10);
+ if (errno || end == buf || tmp < INT_MIN || tmp > INT_MAX)
+ return -1;
+ *out = tmp;
+ return 0;
+}
+
/* A fresh, private cgroup2 mount, returned as an fd to use with *at() calls. */
static inline int mount_cg2_fd(void)
{
@@ -262,6 +284,55 @@ static inline int enter_cgroup(int cgv2_fd, int ctid)
return ret;
}
+/*
+ * Run function in VE cgroup and given namespaces.
+ *
+ * Namespaces are provided via namespace_ctx and entered based on flags mask.
+ *
+ * Return values:
+ * - 0 if function returns zero
+ * - -1 if function returns negative value
+ * - 1 if function returns positive value
+ * - 2 if setup fails
+ */
+static inline int run_in_ve(int cgv2_fd, int ctid, struct namespace_ctx *ctx,
+ int flags, int (*fn)(void *), void *arg)
+{
+ int status;
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0) {
+ fprintf(stderr, "%s: fork failed\n", __func__);
+ return 2;
+ }
+ if (pid == 0) {
+ int ret;
+
+ if (enter_cgroup(cgv2_fd, ctid) < 0) {
+ fprintf(stderr, "%s: enter_cgroup failed\n", __func__);
+ _exit(254);
+ }
+ if (enter_namespaces(ctx, flags) < 0)
+ _exit(254);
+
+ ret = fn(arg);
+ if (ret < 0)
+ ret = 1;
+ else if (ret > 0)
+ ret = 255;
+ _exit(ret);
+ }
+ if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || WEXITSTATUS(status) == 254)
+ return 2;
+ if (WEXITSTATUS(status) == 255)
+ return 1;
+ if (WEXITSTATUS(status))
+ return -1;
+ return 0;
+
+}
+
/*
* 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.
--
2.47.1
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 5+ messages in thread* [Devel] [PATCH VZ10 v3 3/3] ve_namespace: Allow entering non-running VE namespace
2026-09-09 21:32 [Devel] [PATCH VZ10 v3 0/3] Improvements for ve selftests Vladimir Riabchun
2026-09-09 21:32 ` [Devel] [PATCH VZ10 v3 1/3] selftests/ve: Add namespace management helpers Vladimir Riabchun
2026-09-09 21:32 ` [Devel] [PATCH VZ10 v3 2/3] selftests/ve: Add more helpers Vladimir Riabchun
@ 2026-09-09 21:32 ` Vladimir Riabchun
2026-09-15 12:28 ` [Devel] [PATCH VZ10 v3 0/3] Improvements for ve selftests Vasileios Almpanis
3 siblings, 0 replies; 5+ messages in thread
From: Vladimir Riabchun @ 2026-09-09 21:32 UTC (permalink / raw)
To: Konstantin Khorenko, vasileios.almpanis; +Cc: devel
This doesn't bring a lot of value and makes tests more complex.
To enter VE namespace we need to actually start VE.
In vzctl we have locking to prevent parallel creations.
If someone decides to skip locks and start one CT
in parallel - big mistake, but not ours.
https://virtuozzo.atlassian.net/browse/VSTOR-144504
Feature: ve selftests
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
---
kernel/ve/ve_namespace.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/kernel/ve/ve_namespace.c b/kernel/ve/ve_namespace.c
index 3dbe0dde2645..879a7863e33f 100644
--- a/kernel/ve/ve_namespace.c
+++ b/kernel/ve/ve_namespace.c
@@ -227,7 +227,8 @@ static int ve_ns_install(struct nsset *nsset, struct ns_common *new)
return -EPERM;
if (!VE_IS_RUNNING(ve_ns->ve))
- return -EINVAL;
+ pr_warn_once("Switching to a non-running ve %s.\n",
+ ve_name(ve_ns->ve));
/*
* Don't need to put_ve_ns(nsset->ve_ns) here,
--
2.47.1
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [Devel] [PATCH VZ10 v3 0/3] Improvements for ve selftests
2026-09-09 21:32 [Devel] [PATCH VZ10 v3 0/3] Improvements for ve selftests Vladimir Riabchun
` (2 preceding siblings ...)
2026-09-09 21:32 ` [Devel] [PATCH VZ10 v3 3/3] ve_namespace: Allow entering non-running VE namespace Vladimir Riabchun
@ 2026-09-15 12:28 ` Vasileios Almpanis
3 siblings, 0 replies; 5+ messages in thread
From: Vasileios Almpanis @ 2026-09-15 12:28 UTC (permalink / raw)
To: Vladimir Riabchun; +Cc: devel
On Wed, 09 Sep 2026 21:32:47 +0000, Vladimir Riabchun <vladimir.riabchun@virtuozzo.com> wrote:
> Improvements for ve selftests
>
> This change adds helpers for creating and preserving various
> namespaces and running functions inside them.
> This appeared useful while implementing ve mount accounting
> selftests - it required keeping mount namespace alive and
> doing different things at different moments of time.
>
> [...]
Reviewed-by: Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
--
Vasileios Almpanis <vasileios.almpanis@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 5+ messages in thread