The commit is pushed to "branch-rh10-6.12.0-55.13.1.3.x.vz10-ovz" and will
appear at [email protected]:openvz/vzkernel.git
after rh10-6.12.0-55.13.1.3.26.vz10
------>
commit 11a613f030ed03cf5d6386b0c031d93ac2f6192a
Author: Aleksei Oladko <[email protected]>
Date: Fri Dec 19 18:28:56 2025 +0000
tests: add ve_printk selftest
Add selftests for the printk virtualization feature. The new tests cover
ve_printk, ve_printk_ratelimited, net_ve_ratelimited, and log buffer
overflow handling.
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
Signed-off-by: Aleksei Oladko <[email protected]>
Reviewed-by: Pavel Tikhomirov <[email protected]>
Feature: printk: per-CT virtualization
---
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 +
tools/testing/selftests/ve_printk/ve_printk_test.c | 689 +++++++++++++++++++++
6 files changed, 718 insertions(+)
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 363d031a16f7e..7334fb2076768 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 0000000000000..a4ad6620b8032
--- /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 0000000000000..e3edcbacda1ed
--- /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 0000000000000..cdc89068ca069
--- /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 0000000000000..b774e2b9484c8
--- /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 0000000000000..d5733b9e4aaa0
--- /dev/null
+++ b/tools/testing/selftests/ve_printk/ve_printk_test.c
@@ -0,0 +1,689 @@
+// 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)
+#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) != 0 && errno == ENOENT) {
+ break;
+ }
+ self->ctid++;
+ }
+ ASSERT_LT(self->ctid, CTID_MAX);
+
+ 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);
+}
+
+int setup_timens(void)
+{
+ int ret, fd;
+
+ if (access("/proc/self/timens_offsets", F_OK))
+ return 0;
+
+ if (unshare(CLONE_NEWTIME)) {
+ return -1;
+ }
+
+ fd = open("/proc/self/ns/time_for_children", O_RDONLY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ ret = setns(fd, CLONE_NEWTIME);
+
+ close(fd);
+ return ret;
+}
+
+struct fargs {
+ char *cgve;
+ int ctid;
+ int test;
+};
+
+enum {
+ TEST_LOG,
+ TEST_LOGV0,
+ TEST_LOGBOTH,
+ TEST_PR_RATELIMIT,
+ TEST_OVERFLOW,
+};
+
+int ve_printk_test_logct(void)
+{
+ FILE *pdmesg;
+ char buf[1024];
+ char str[256];
+ FILE *f;
+ int pid;
+ int ret = 2;
+
+ f = popen("./" SEGFAULT_PROG, "r");
+ if (!f)
+ return 1;
+
+ if (fscanf(f, "%d", &pid) != 1) {
+ pclose(f);
+ return 1;
+ }
+ pclose(f);
+
+ pdmesg = popen("dmesg", "r");
+ if (!pdmesg)
+ return 1;
+
+ snprintf(str, sizeof(str), "%s[%d]: segfault at", SEGFAULT_PROG, pid);
+ while (fgets(buf, sizeof(buf), pdmesg)) {
+ if (ret == 2 && strstr(buf, str)) {
+ ret = 1;
+ }
+ if (ret == 1 && strstr(buf, "Code: ")) {
+ ret = 0;
+ break;
+ }
+ }
+ pclose(pdmesg);
+ return ret;
+}
+
+int ve_printk_test_logve0(void)
+{
+ FILE *pdmesg;
+ char buf[1024];
+ int ret = 0;
+
+ /* do_trap -> show_signal -> print_vma_addr ->
ve_print_vma_addr(VE0_LOG, ... */
+ ret = system("./" TRAP_PROG);
+ if (ret < 0)
+ return 1;
+ ret = 0;
+
+ pdmesg = popen("dmesg", "r");
+ if (!pdmesg)
+ return 1;
+
+ /* There should be no trap messages in the container's dmesg */
+ while (fgets(buf, sizeof(buf), pdmesg)) {
+ if (strstr(buf, "traps: test_trap[") &&
+ strstr(buf, " in test_trap[")) {
+ ret = 1;
+ break;
+ }
+ }
+ pclose(pdmesg);
+ return ret;
+}
+
+int ve_printk_test_logboth(void)
+{
+ FILE *pdmesg;
+ char buf[1024];
+ int ret;
+
+ ret = system("ip link set up dev lo");
+ if (ret < 0)
+ return 1;
+ /*
+ * Reduce the connection tracking table size to 2. After two ping calls
the
+ * table will be full, and all new packets will trigger
+ * net_veboth_ratelimited. This parameter is modified inside the
container.
+ */
+ ret = system("echo 2 > /proc/sys/net/nf_conntrack_max");
+ if (ret < 0)
+ return 1;
+ /* The rule is added inside the container */
+ ret = system("iptables -A INPUT -m conntrack --ctstate
RELATED,ESTABLISHED -j ACCEPT");
+ if (ret < 0)
+ return 1;
+ ret = system("ping -q -c 1 -w 1 127.0.0.1 > /dev/null");
+ if (ret < 0)
+ return 1;
+ ret = system("ping -q -c 1 -w 1 127.0.0.1 > /dev/null");
+ if (ret < 0)
+ return 1;
+ /* triggers net_veboth_ratelimited 100 times */
+ ret = system("ping -q -c 100 -w 1 -i 0.0001 127.0.0.1 > /dev/null");
+ if (ret < 0)
+ return 1;
+
+ pdmesg = popen("dmesg", "r");
+ if (!pdmesg)
+ return 1;
+
+ ret = TEST_RATELIMIT_BURST;
+ /* verify that only 10 records were added */
+ while (fgets(buf, sizeof(buf), pdmesg)) {
+ if (strstr(buf, "nf_conntrack table full, dropping packet")) {
+ ret--;
+ }
+ }
+ pclose(pdmesg);
+ return ret;
+}
+
+int ve_printk_test_ratelimit(void)
+{
+ FILE *pdmesg;
+ char buf[1024];
+ int ret;
+ int suppressed = 1;
+ int i;
+
+ /*
+ * Attempting to add this rule results in an error message being written
+ * to the container's dmesg. The error is logged using a ratelimited
printk:
+ * reject_tg_check -> ve_printk_ratelimited(VE_LOG, ...
+ */
+ for (i = 0; i < 2 * TEST_RATELIMIT_BURST; i++) {
+ /* The rule is added inside the container */
+ ret = system("iptables -A INPUT -s 10.20.30.40 -j REJECT
--reject-with tcp-reset &> /dev/null");
+ if (ret < 0)
+ suppressed++;
+ }
+ if (suppressed >= TEST_RATELIMIT_BURST)
+ return 1;
+ sleep(TEST_RATELIMIT + 1);
+ ret = system("iptables -A INPUT -s 10.20.30.40 -j REJECT --reject-with
tcp-reset &> /dev/null");
+ if (ret < 0)
+ return 1;
+
+ ret = TEST_RATELIMIT_BURST + 1;
+ suppressed = 1;
+ pdmesg = popen("dmesg", "r");
+ if (!pdmesg)
+ return 1;
+
+ /*
+ * verify that only 10 records were added and check for the
+ * "callbacks suppressed" message in dmesg
+ */
+ while (fgets(buf, sizeof(buf), pdmesg)) {
+ if (strstr(buf, "TCP_RESET invalid for non-tcp")) {
+ ret--;
+ }
+ if (strstr(buf, "reject_tg_check") && strstr(buf, "callbacks
suppressed"))
+ suppressed--;
+ }
+ pclose(pdmesg);
+
+ return ret + suppressed;
+}
+
+int ve_printk_test_overflow(void)
+{
+ int i;
+ int ret, cnt = 0;
+
+ /*
+ * Ten program segfaults are enough to overflow the container's log
buffer
+ * Run 15 to ensure the buffer is actually overflowed.
+ */
+ for (i = 0; i < 15; i++) {
+ ret = system("./" SEGFAULT_PROG " > /dev/null");
+ if (ret < 0)
+ continue;
+ cnt++;
+ /* bypass printk_ratelimit */
+ if (i == 9)
+ sleep(5);
+ }
+ /* Log buffer did not overflow */
+ if (cnt < 10)
+ return 1;
+ return ve_printk_test_logct();
+}
+
+int child_func(void *arg)
+{
+ int ret;
+ int fd;
+ struct fargs *args = (struct fargs *)arg;
+ char cg_state[PATH_MAX];
+
+ ret = setup_timens();
+ if (ret)
+ return ret;
+
+ snprintf(cg_state, sizeof(cg_state), "%s/%d/ve.state", args->cgve,
args->ctid);
+
+ (void)umount2("/proc", MNT_DETACH);
+ ret = mount("proc", "/proc", "proc", 0, NULL);
+ if (ret < 0)
+ return 1;
+
+ fd = open("/proc/self/uid_map", O_WRONLY);
+ if (fd < 0)
+ return 1;
+ ret = write(fd, "0 0 1\n", 6);
+ if (ret < 0 || ret != 6) {
+ close(fd);
+ return 1;
+ }
+ close(fd);
+
+ fd = open(cg_state, O_WRONLY);
+ if (fd < 0) {
+ return 1;
+ }
+
+ ret = write(fd, "START", strlen("START"));
+ if (ret < 0 || ret != strlen("START")) {
+ close(fd);
+ return 1;
+ }
+
+ close(fd);
+
+ switch (args->test) {
+ case TEST_LOG:
+ ret = ve_printk_test_logct();
+ break;
+ case TEST_LOGV0:
+ ret = ve_printk_test_logve0();
+ break;
+ case TEST_LOGBOTH:
+ ret = ve_printk_test_logboth();
+ break;
+ case TEST_PR_RATELIMIT:
+ ret = ve_printk_test_ratelimit();
+ break;
+ case TEST_OVERFLOW:
+ ret = ve_printk_test_overflow();
+ break;
+ default:
+ ret = 1;
+ }
+ return ret;
+}
+
+int enter_cgroup(const char *cgroup, int ctid)
+{
+ char cg_path[PATH_MAX];
+ char pid_str[64];
+ int fd;
+ int ret = 0;
+
+ if (ctid)
+ snprintf(cg_path, sizeof(cg_path), "%s/%d/cgroup.procs",
cgroup, ctid);
+ else
+ snprintf(cg_path, sizeof(cg_path), "%s/cgroup.procs", cgroup);
+ snprintf(pid_str, sizeof(pid_str), "%d", getpid());
+ fd = open(cg_path, O_WRONLY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ ret = write(fd, pid_str, strlen(pid_str));
+ if (ret < 0 || ret != strlen(pid_str)) {
+ ret = -1;
+ }
+
+ close(fd);
+ return ret;
+}
+
+int run_vzct(char *cgv2, int ctid, int testid)
+{
+ pid_t pid;
+ struct fargs args = {
+ .cgve = cgv2,
+ .ctid = ctid,
+ .test = testid,
+ };
+ struct clone_args cargs = {
+ .flags = CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
+ CLONE_NEWPID | CLONE_NEWUSER | CLONE_NEWNET |
+ CLONE_NEWCGROUP | CLONE_NEWVE,
+ .exit_signal = SIGCHLD,
+ };
+ int status;
+ int ret;
+
+ ret = enter_cgroup(cgv2, ctid);
+ if (ret < 0)
+ return -1;
+
+ pid = syscall(__NR_clone3, &cargs, sizeof(cargs));
+ if (pid < 0)
+ return -1;
+ if (pid == 0)
+ _exit(child_func((void *)&args));
+
+ ret = waitpid(pid, &status, 0);
+ if (ret < 0)
+ return -1;
+ ret = -1;
+ if (WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ }
+ enter_cgroup(cgv2, 0);
+
+ return ret;
+}
+
+/*
+ * Open /dev/kmsg and skip all existing messages so that
+ * after the test only messages generated during the test
+ * can be read
+ */
+FILE *open_dmesg(void)
+{
+ int fd;
+ FILE *fdmesg;
+ char buf[1024];
+
+ fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
+ if (fd < 0)
+ return NULL;
+ fdmesg = fdopen(fd, "r");
+ if (!fdmesg)
+ return NULL;
+
+ while (fgets(buf, sizeof(buf), fdmesg));
+ return fdmesg;
+}
+
+int restore_param(const char *path, int val)
+{
+ int fd;
+ char str[16];
+ int ret;
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0)
+ return -1;
+ snprintf(str, sizeof(str), "%d", val);
+ ret = write(fd, str, strlen(str));
+ close(fd);
+
+ return ret == strlen(str) ? 0 : -1;
+}
+
+int set_param(const char *path, int val, int *old)
+{
+ FILE *f;
+
+ f = fopen(path, "r");
+ if (!f)
+ return -1;
+ if (fscanf(f, "%u", old) != 1) {
+ fclose(f);
+ return -1;
+ }
+ fclose(f);
+ if (*old == val)
+ return 0;
+ return restore_param(path, val);
+}
+
+/*
+ * Test verifies that segfault messages are logged only to the container
(VE_LOG),
+ * not to VE0 (host). Inside the container, test_segf program is run which
causes
+ * a segfault. The test verifies that there are NO segfault messages from this
+ * process on the host - they should be visible only inside the container.
+ */
+TEST_F(ve_printk, ve_log)
+{
+ int ret;
+ FILE *fdmesg;
+ char buf[1024];
+ int old;
+
+ ASSERT_EQ(set_param("/proc/sys/debug/exception-trace", 1, &old), 0);
+ fdmesg = open_dmesg();
+ ASSERT_NE(fdmesg, NULL);
+
+ ret = run_vzct(self->cgv2_path, self->ctid, TEST_LOG);
+ ASSERT_EQ(ret, 0);
+
+ while (fgets(buf, sizeof(buf), fdmesg)) {
+ ASSERT_EQ(strstr(buf, SEGFAULT_PROG) && strstr(buf, "segfault
at"), false);
+ EXPECT_EQ(strstr(buf, "Code: "), NULL);
+ }
+ fclose(fdmesg);
+ restore_param("/proc/sys/debug/exception-trace", old);
+}
+
+/*
+ * Test verifies that trap messages are logged to VE0 (host) via VE0_LOG.
+ * Inside the container, test_trap program is run which triggers int3
(breakpoint).
+ * The test verifies that there ARE trap messages from this process on the
host -
+ * they should be visible in VE0, as some critical messages must be logged at
+ * the host level regardless of the container.
+ */
+TEST_F(ve_printk, ve0_log)
+{
+ int ret;
+ FILE *fdmesg;
+ char buf[1024];
+ int ex_trace;
+
+ ASSERT_EQ(set_param("/proc/sys/debug/exception-trace", 1, &ex_trace),
0);
+ fdmesg = open_dmesg();
+ ASSERT_NE(fdmesg, NULL);
+
+ ret = run_vzct(self->cgv2_path, self->ctid, TEST_LOGV0);
+ ASSERT_EQ(ret, 0);
+
+ while (fgets(buf, sizeof(buf), fdmesg)) {
+ if (strstr(buf, "traps: test_trap[") &&
+ strstr(buf, " in test_trap[")) {
+ ret = 1;
+ break;
+ }
+ }
+ fclose(fdmesg);
+ restore_param("/proc/sys/debug/exception-trace", ex_trace);
+ ASSERT_EQ(ret, 1);
+}
+
+/*
+ * Test verifies net_veboth_ratelimited function which logs messages
simultaneously
+ * to both the container (VE_LOG) and VE0 (VE0_LOG), but with ratelimit
throttling.
+ * Inside the container, a small conntrack table (size 2) is configured, then
many ping
+ * packets are sent, causing table overflow and generation of "nf_conntrack
table full,
+ * dropping packet" messages. The test verifies that exactly
TEST_RATELIMIT_BURST (10)
+ * messages appeared on the host - ratelimit should limit the number of
messages even
+ * if more were generated.
+ */
+TEST_F(ve_printk, ve_log_both)
+{
+ int ret;
+ FILE *fdmesg;
+ char buf[1024];
+ int old_ratelimit;
+
+ ASSERT_EQ(set_param("/proc/sys/net/core/message_burst",
+ TEST_RATELIMIT_BURST, &old_ratelimit), 0);
+ fdmesg = open_dmesg();
+ ASSERT_NE(fdmesg, NULL);
+
+ ret = run_vzct(self->cgv2_path, self->ctid, TEST_LOGBOTH);
+ ASSERT_EQ(ret, 0);
+
+ while (fgets(buf, sizeof(buf), fdmesg)) {
+ if (strstr(buf, "nf_conntrack table full, dropping packet")) {
+ ret++;
+ }
+ }
+ fclose(fdmesg);
+ restore_param("/proc/sys/net/core/message_burst", old_ratelimit);
+ ASSERT_EQ(ret, TEST_RATELIMIT_BURST);
+}
+
+/*
+ * Test verifies ve_printk_ratelimited(VE_LOG) function which logs messages
only
+ * to the container with ratelimit throttling. Inside the container, many
invalid
+ * iptables rules are created (REJECT with tcp-reset for non-tcp), which
generates
+ * warnings via ve_printk_ratelimited. The test verifies that there are NO such
+ * messages on the host (they should be visible only inside the container), and
+ * that ratelimit works correctly (messages are suppressed after exceeding the
limit).
+ */
+TEST_F(ve_printk, ve_printk_ratelimited)
+{
+ int ret;
+ FILE *fdmesg;
+ char buf[1024];
+ int old_ratelimit_burst;
+ int old_ratelimit;
+
+ ASSERT_EQ(set_param("/proc/sys/net/core/message_burst",
+ TEST_RATELIMIT_BURST, &old_ratelimit_burst), 0);
+ ASSERT_EQ(set_param("/proc/sys/net/core/message_cost",
+ TEST_RATELIMIT, &old_ratelimit), 0);
+ fdmesg = open_dmesg();
+ ASSERT_NE(fdmesg, NULL);
+
+ ret = run_vzct(self->cgv2_path, self->ctid, TEST_PR_RATELIMIT);
+ ASSERT_EQ(ret, 0);
+
+ while (fgets(buf, sizeof(buf), fdmesg)) {
+ ASSERT_EQ(strstr(buf, "reject_tg_check") && strstr(buf,
"callbacks suppressed"), false);
+ ASSERT_EQ(strstr(buf, "TCP_RESET invalid for non-tcp"), NULL);
+ }
+ fclose(fdmesg);
+ restore_param("/proc/sys/net/core/message_burst", old_ratelimit_burst);
+ restore_param("/proc/sys/net/core/message_cost", old_ratelimit);
+}
+
+/*
+ * Test verifies that when the container's log buffer overflows, messages are
still
+ * handled correctly. Inside the container, 15 segfaults are triggered (enough
to
+ * overflow the buffer), with a sleep after the 9th to bypass printk_ratelimit.
+ * The test verifies that even after buffer overflow, the logging mechanism
continues
+ * to work properly and messages are still processed.
+ */
+TEST_F(ve_printk, ve_printk_overflow)
+{
+ int ret;
+ int ex_trace;
+
+ ASSERT_EQ(set_param("/proc/sys/debug/exception-trace", 1, &ex_trace),
0);
+
+ ret = run_vzct(self->cgv2_path, self->ctid, TEST_OVERFLOW);
+ ASSERT_EQ(ret, 0);
+ restore_param("/proc/sys/debug/exception-trace", ex_trace);
+
+}
+
+TEST_HARNESS_MAIN
_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel