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

Reply via email to