Except for small formating nits it looks good. Consider it Reviewed-by: Pavel Tikhomirov <[email protected]>
after formating is fixed. (e.g. you can add Reviewed-by line to the patch directly when you send v6) On 12/20/25 02: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) > +#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++; > + } > + ASSERT_LT(self->ctid, CTID_MAX); > + > + snprintf(path, sizeof(path), "%s/%d", self->cgv2_path, self->ctid); > + 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) { switch (args->test) { ^ Missing space after switch > + 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, > + }; Should use tab indent in the above line instead of 8-spaces, also I see multiple other places (5 total). > + 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)) ; don't really need space before ';' linux$ git grep "while.*\s;$" | grep -v } | wc -l 49 linux$ git grep "while.*\S;$" | grep -v } | wc -l 1247 preferable style is without space. > + 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 -- Best regards, Pavel Tikhomirov Senior Software Developer, Virtuozzo. _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
