On 12/19/25 19:28, Aleksei Oladko wrote:
Add selftests for the printk virtualization feature. The new tests cover
ve_printk, ve_printk_ratelimited, net_ve_ratelimited, and log buffer
overflow handling.
Entering the VE cgroup v1 is required until it is switched to cgroup v2.
Test functions (executed inside containers):
- ve_printk_test_logct(): Verifies segfault messages are logged to
container
- ve_printk_test_logve0(): Verifies trap messages are logged to VE0
- ve_printk_test_logboth(): Verifies net_veboth_ratelimited messages
- ve_printk_test_ratelimit(): Verifies ve_printk_ratelimited(VE_LOG) messages
- ve_printk_test_overflow(): Verifies log buffer overflow handling
https://virtuozzo.atlassian.net/browse/VSTOR-114252
v2:
- added checks for the return values of system() and write()
- added comments to clarify the code
- removed the hunk that set up the gid mapping,
as it is no required for creating the test container
v3:
- added entering the VE namespace
- fixed typos
v4: fixed typos
v5:
- switched self-attachment to the ve controller from cgroup v1 to cgroup v2
- made child_func return a posiive exit code on failure.
- revorked entering the ve namespace to use clone 3 instead of unshare
Signed-off-by: Aleksei Oladko <[email protected]>
---
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/ve_printk/.gitignore | 3 +
tools/testing/selftests/ve_printk/Makefile | 8 +
tools/testing/selftests/ve_printk/test_segf.c | 12 +
tools/testing/selftests/ve_printk/test_trap.c | 5 +
.../selftests/ve_printk/ve_printk_test.c | 693 ++++++++++++++++++
6 files changed, 722 insertions(+)
create mode 100644 tools/testing/selftests/ve_printk/.gitignore
create mode 100644 tools/testing/selftests/ve_printk/Makefile
create mode 100644 tools/testing/selftests/ve_printk/test_segf.c
create mode 100644 tools/testing/selftests/ve_printk/test_trap.c
create mode 100644 tools/testing/selftests/ve_printk/ve_printk_test.c
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 363d031a16f7..7334fb207676 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -113,6 +113,7 @@ TARGETS += tty
TARGETS += uevent
TARGETS += user_events
TARGETS += vDSO
+TARGETS += ve_printk
TARGETS += mm
TARGETS += x86
TARGETS += zram
diff --git a/tools/testing/selftests/ve_printk/.gitignore
b/tools/testing/selftests/ve_printk/.gitignore
new file mode 100644
index 000000000000..a4ad6620b803
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/.gitignore
@@ -0,0 +1,3 @@
+ve_printk_test
+test_segf
+test_trap
diff --git a/tools/testing/selftests/ve_printk/Makefile
b/tools/testing/selftests/ve_printk/Makefile
new file mode 100644
index 000000000000..e3edcbacda1e
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for ve_printk selftests.
+CFLAGS = -g -I../../../../usr/include/ -Wall -O2
+
+TEST_GEN_PROGS += ve_printk_test
+TEST_GEN_FILES += test_segf test_trap
+
+include ../lib.mk
diff --git a/tools/testing/selftests/ve_printk/test_segf.c
b/tools/testing/selftests/ve_printk/test_segf.c
new file mode 100644
index 000000000000..cdc89068ca06
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/test_segf.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void)
+{
+ int *p = (int *)0xffffffffffffff00;
+
+ printf("%d\n", getpid());
+ fflush(stdout);
+ *p = 1;
+ return 0;
+}
diff --git a/tools/testing/selftests/ve_printk/test_trap.c
b/tools/testing/selftests/ve_printk/test_trap.c
new file mode 100644
index 000000000000..b774e2b9484c
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/test_trap.c
@@ -0,0 +1,5 @@
+int main(void)
+{
+ __asm__("int3");
+ return 0;
+}
diff --git a/tools/testing/selftests/ve_printk/ve_printk_test.c
b/tools/testing/selftests/ve_printk/ve_printk_test.c
new file mode 100644
index 000000000000..e150366ef580
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/ve_printk_test.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ve_printk selftests
+ *
+ * This file contains tests for the VE printk virtualization feature. It
verifies
+ * that kernel messages are correctly routed to containers (VE_LOG), host
(VE0_LOG),
+ * or both (VE_LOG_BOTH), and that rate limiting works correctly.
+ *
+ * Test functions (executed inside containers):
+ * - ve_printk_test_logct(): Verifies segfault messages are logged to
container
+ * - ve_printk_test_logve0(): Verifies trap messages are logged to VE0
+ * - ve_printk_test_logboth(): Verifies net_veboth_ratelimited messages
+ * - ve_printk_test_ratelimit(): Verifies ve_printk_ratelimited(VE_LOG)
messages
+ * - ve_printk_test_overflow(): Verifies log buffer overflow handling
+ *
+ * Test cases:
+ * - TEST_F(ve_printk, ve_log): Verifies VE_LOG routing
(segfault)
+ * - TEST_F(ve_printk, ve0_log): Verifies VE0_LOG routing (trap)
+ * - TEST_F(ve_printk, ve_log_both): Verifies VE_LOG_BOTH + ratelimit
+ * - TEST_F(ve_printk, ve_printk_ratelimited): Verifies VE_LOG ratelimit
+ * - TEST_F(ve_printk, ve_printk_overflow): Verifies buffer overflow
handling
+ */
+#define _GNU_SOURCE
+#include <linux/sched.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <linux/limits.h>
+#include <errno.h>
+
+#include "../kselftest_harness.h"
+
+#define __STACK_SIZE (8 * 1024 * 1024)
non-used define, i'll drop it
+#define CTID_MIN 108
+#define CTID_MAX 200
+#define SEGFAULT_PROG "test_segf"
+#define TRAP_PROG "test_trap"
+#define TEST_RATELIMIT_BURST 10
+#define TEST_RATELIMIT 5
+
+static int has_substr(char *buf, const char *str)
+{
+ char *token;
+ char *str_ptr = buf;
+
+ while ((token = strsep(&str_ptr, ",")) != NULL) {
+ if (!strcmp(token, str))
+ return 1;
+ }
+ return 0;
+}
+
+static int get_mount_path(const char *fstype, const char *subsys, char *out,
int size)
+{
+ FILE *fp;
+ int n;
+ char buf[PATH_MAX];
+ char target[4096];
+ char ops[4096];
+ char format[4096];
+ int ret = 1;
+
+ snprintf(format, sizeof(format), "%%*s %%4095s %s %%4095s", fstype);
+
+ fp = fopen("/proc/mounts", "r");
+ if (fp == NULL)
+ return -1;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ n = sscanf(buf, format, target, ops);
+ if (n != 2)
+ continue;
+ if (subsys == NULL || has_substr(ops, subsys)) {
+ strncpy(out, target, size);
+ out[size-1] = '\0';
+ ret = 0;
+ break;
+ }
+ }
+ fclose(fp);
+
+ return ret;
+}
+
+FIXTURE(ve_printk)
+{
+ char cgv2_path[PATH_MAX];
+ int ctid;
+};
+
+FIXTURE_SETUP(ve_printk)
+{
+ char path[PATH_MAX * 2];
+
+ ASSERT_EQ(get_mount_path("cgroup2", NULL, self->cgv2_path,
sizeof(self->cgv2_path)), 0);
+
+ self->ctid = CTID_MIN;
+ while (self->ctid < CTID_MAX) {
+ snprintf(path, sizeof(path), "%s/%d", self->cgv2_path,
self->ctid);
+ if (access(path, F_OK) && errno == ENOENT) {
+ snprintf(path, sizeof(path), "%s/%d", self->cgv2_path,
self->ctid);
+ if (access(path, F_OK)) {
+ break;
+ }
+ }
+ self->ctid++;
+ }
Explanation of the Fix
Original code (lines 108-112):
1 │if (access(path, F_OK) && errno == ENOENT) {
2 │ snprintf(path, sizeof(path), "%s/%d", self->cgv2_path, self->ctid);
3 │ if (access(path, F_OK)) {
4 │ break;
5 │ }
6 │}
Problems:
1. Incorrect condition: access(path, F_OK) && errno == ENOENT is wrong. access() returns 0 on
success (file exists) and
-1 on failure (file doesn't exist). In C, access(path, F_OK) in an if is true when non-zero
(i.e., -1). So the
condition means "file doesn't exist AND errno == ENOENT", but errno is only valid when access()
returns -1. The
check is redundant and confusing.
2. Code duplication: snprintf() and access() are called twice with the same arguments, which is
unnecessary.
Fixed code:
1 │if (access(path, F_OK) != 0 && errno == ENOENT) {
2 │ break;
3 │}
Why this is correct:
• access(path, F_OK) != 0 explicitly checks for failure (file doesn't exist).
• errno == ENOENT confirms the specific error (file/directory not found).
• Removed duplicate snprintf() and access() calls.
• The logic is now clear: if the path doesn't exist (ENOENT), we found a free ctid and can break
out of the loop.
This correctly finds an available container ID by checking that the cgroup
path doesn't exist yet.
i will handle this.
+ ASSERT_LT(self->ctid, CTID_MAX);
+
+ snprintf(path, sizeof(path), "%s/%d", self->cgv2_path, self->ctid);
This line is redundant i suppose.
i will remove it.
+ ASSERT_EQ(mkdir(path, 0755), 0);
+
+ snprintf(path, sizeof(path), "echo -ve >
%s/%d/cgroup.controllers_hidden",
+ self->cgv2_path, self->ctid);
+ ASSERT_EQ(system(path), 0);
+
+ snprintf(path, sizeof(path), "echo %d > %s/%d/ve.veid",
+ self->ctid, self->cgv2_path, self->ctid);
+ ASSERT_EQ(system(path), 0);
+};
+
+FIXTURE_TEARDOWN(ve_printk)
+{
+ char path[PATH_MAX * 2];
+
+ snprintf(path, sizeof(path), "%s/%d/vz.slice", self->cgv2_path,
self->ctid);
+ rmdir(path);
+ snprintf(path, sizeof(path), "%s/%d", self->cgv2_path, self->ctid);
+ rmdir(path);
+}
+
...
_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel