From patchwork Mon Jul 20 11:37:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [vz8] ve/printk: Fix printk virtualization From: Andrey Ryabinin X-Patchwork-Id: 13175 Message-Id: <20200720113743.24533-1-aryabinin@virtuozzo.com> To: devel@openvz.org Date: Mon, 20 Jul 2020 14:37:43 +0300 ve_printk() corrupts host's dmesg: # dmesg|wc -l 599 # vzctl create 101 # vzctl set 101 --netif_add eth0 --save # vzctl start 101 # vzctl exec 101 'tcpdump -w tcpdump.out -U -n -i eth0 esp' # dmesg|wc -l 2 Add missing parts of prinkt virtualization to fix this. https://jira.sw.ru/browse/PSBM-17899 https://jira.sw.ru/browse/PSBM-105442 Fixes: 7c0dae2429 ("ve/printk: printk virtualization") Signed-off-by: Andrey Ryabinin --- kernel/printk/printk.c | 99 ++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 4cccbba92b6c..b55fdd7b25ea 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -530,23 +530,23 @@ static char *log_dict(const struct printk_log *msg) } /* get record by index; idx must point to valid msg */ -static struct printk_log *log_from_idx(u32 idx) +static struct printk_log *log_from_idx(struct log_state *log, u32 idx) { - struct printk_log *msg = (struct printk_log *)(log_buf + idx); + struct printk_log *msg = (struct printk_log *)(log->buf + idx); /* * A length == 0 record is the end of buffer marker. Wrap around and * read the message at the start of the buffer. */ if (!msg->len) - return (struct printk_log *)log_buf; + return (struct printk_log *)log->buf; return msg; } /* get next record; idx must point to valid msg */ -static u32 log_next(u32 idx) +static u32 log_next(struct log_state *log, u32 idx) { - struct printk_log *msg = (struct printk_log *)(log_buf + idx); + struct printk_log *msg = (struct printk_log *)(log->buf + idx); /* length == 0 indicates the end of the buffer; wrap */ /* @@ -555,7 +555,7 @@ static u32 log_next(u32 idx) * return the one after that. */ if (!msg->len) { - msg = (struct printk_log *)log_buf; + msg = (struct printk_log *)log->buf; return msg->len; } return idx + msg->len; @@ -593,7 +593,7 @@ static int log_make_free_space(struct log_state *log, while (log->first_seq < log->next_seq && !logbuf_has_space(log, msg_size, false)) { /* drop old messages until we have enough contiguous space */ - log->first_idx = log_next(log->first_idx); + log->first_idx = log_next(log, log->first_idx); log->first_seq++; } @@ -677,12 +677,12 @@ static int log_store(struct log_state *log, * at the end of the buffer. Add an empty header with len == 0 * to signify a wrap around. */ - memset(log_buf + log->next_idx, 0, sizeof(struct printk_log)); + memset(log->buf + log->next_idx, 0, sizeof(struct printk_log)); log->next_idx = 0; } /* fill message */ - msg = (struct printk_log *)(log_buf + log->next_idx); + msg = (struct printk_log *)(log->buf + log->next_idx); memcpy(log_text(msg), text, text_len); msg->text_len = text_len; if (trunc_msg_len) { @@ -930,14 +930,14 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, goto out; } - msg = log_from_idx(user->idx); + msg = log_from_idx(log, user->idx); len = msg_print_ext_header(user->buf, sizeof(user->buf), msg, user->seq); len += msg_print_ext_body(user->buf + len, sizeof(user->buf) - len, log_dict(msg), msg->dict_len, log_text(msg), msg->text_len); - user->idx = log_next(user->idx); + user->idx = log_next(log, user->idx); user->seq++; logbuf_unlock_irq(); @@ -1389,11 +1389,11 @@ static int syslog_print(struct log_state *log, } skip = log->syslog_partial; - msg = log_from_idx(log->syslog_idx); + msg = log_from_idx(log, log->syslog_idx); n = msg_print_text(msg, true, text, LOG_LINE_MAX + PREFIX_MAX); if (n - log->syslog_partial <= size) { /* message fits into buffer, move forward */ - log->syslog_idx = log_next(log->syslog_idx); + log->syslog_idx = log_next(log, log->syslog_idx); log->syslog_seq++; n -= log->syslog_partial; log->syslog_partial = 0; @@ -1446,10 +1446,10 @@ static int syslog_print_all(struct log_state *log, seq = log->clear_seq; idx = log->clear_idx; while (seq < log->next_seq) { - struct printk_log *msg = log_from_idx(idx); + struct printk_log *msg = log_from_idx(log, idx); len += msg_print_text(msg, true, NULL, 0); - idx = log_next(idx); + idx = log_next(log, idx); seq++; } @@ -1457,10 +1457,10 @@ static int syslog_print_all(struct log_state *log, seq = log->clear_seq; idx = log->clear_idx; while (len > size && seq < log->next_seq) { - struct printk_log *msg = log_from_idx(idx); + struct printk_log *msg = log_from_idx(log, idx); len -= msg_print_text(msg, true, NULL, 0); - idx = log_next(idx); + idx = log_next(log, idx); seq++; } @@ -1469,7 +1469,7 @@ static int syslog_print_all(struct log_state *log, len = 0; while (len >= 0 && seq < next_seq) { - struct printk_log *msg = log_from_idx(idx); + struct printk_log *msg = log_from_idx(log, idx); int textlen; textlen = msg_print_text(msg, true, text, @@ -1478,7 +1478,7 @@ static int syslog_print_all(struct log_state *log, len = textlen; break; } - idx = log_next(idx); + idx = log_next(log, idx); seq++; logbuf_unlock_irq(); @@ -1597,10 +1597,10 @@ int do_syslog(int type, char __user *buf, int len, int source) u32 idx = log->syslog_idx; while (seq < log->next_seq) { - struct printk_log *msg = log_from_idx(idx); + struct printk_log *msg = log_from_idx(log, idx); error += msg_print_text(msg, true, NULL, 0); - idx = log_next(idx); + idx = log_next(log, idx); seq++; } error -= log->syslog_partial; @@ -1892,6 +1892,20 @@ static size_t log_output(struct log_state *log, return log_store(log, facility, level, lflags, 0, dict, dictlen, text, text_len); } +static int log_state_init(struct log_state *log) +{ +#ifdef CONFIG_VE + if (log->buf) + return 0; + + log->buf = kzalloc(log->buf_len, GFP_ATOMIC); + if (!log->buf) + return -ENOMEM; +#endif + return 0; +} + + asmlinkage int vprintk_emit_log(struct log_state *log, int facility, int level, const char *dict, size_t dictlen, @@ -1904,6 +1918,8 @@ asmlinkage int vprintk_emit_log(struct log_state *log, unsigned long flags; int printed_len; bool in_sched = false; + bool need_wake = false; + int err; if (level == LOGLEVEL_SCHED) { level = LOGLEVEL_DEFAULT; @@ -1915,6 +1931,13 @@ asmlinkage int vprintk_emit_log(struct log_state *log, /* This stops the holder of console_sem just where we want him */ logbuf_lock_irqsave(flags); + + err = log_state_init(log); + if (err) { + logbuf_unlock_irqrestore(flags); + return err; + } + /* * The printf needs to come first; we need the syslog * prefix which might be passed-in as a parameter. @@ -1972,9 +1995,19 @@ asmlinkage int vprintk_emit_log(struct log_state *log, * semaphore. The release will print out buffers and wake up * /dev/kmsg and syslog() users. */ - if (console_trylock_spinning()) + if (log != &init_log_state) { + logbuf_lock_irqsave(flags); + if (log->seen_seq != log->next_seq && !oops_in_progress) { + log->seen_seq = log->next_seq; + need_wake = true; + } + logbuf_unlock_irqrestore(flags); + } else if (console_trylock_spinning()) console_unlock(); preempt_enable(); + + if (need_wake) + wake_up_interruptible(&log->wait); } wake_up_klogd(); @@ -2465,14 +2498,14 @@ void console_unlock(void) if (log->console_seq == log->next_seq) break; - msg = log_from_idx(log->console_idx); + msg = log_from_idx(log, log->console_idx); if (suppress_message_printing(msg->level)) { /* * Skip record we have buffered and already printed * directly to the console when we received it, and * record that has level above the console loglevel. */ - log->console_idx = log_next(log->console_idx); + log->console_idx = log_next(log, log->console_idx); log->console_seq++; goto skip; } @@ -2490,7 +2523,7 @@ void console_unlock(void) log_dict(msg), msg->dict_len, log_text(msg), msg->text_len); } - log->console_idx = log_next(log->console_idx); + log->console_idx = log_next(log, log->console_idx); log->console_seq++; raw_spin_unlock(&logbuf_lock); @@ -3200,10 +3233,10 @@ bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper, bool syslog, if (dumper->cur_seq >= log->next_seq) goto out; - msg = log_from_idx(dumper->cur_idx); + msg = log_from_idx(log, dumper->cur_idx); l = msg_print_text(msg, syslog, line, size); - dumper->cur_idx = log_next(dumper->cur_idx); + dumper->cur_idx = log_next(log, dumper->cur_idx); dumper->cur_seq++; ret = true; out: @@ -3294,10 +3327,10 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog, seq = dumper->cur_seq; idx = dumper->cur_idx; while (seq < dumper->next_seq) { - struct printk_log *msg = log_from_idx(idx); + struct printk_log *msg = log_from_idx(log, idx); l += msg_print_text(msg, true, NULL, 0); - idx = log_next(idx); + idx = log_next(log, idx); seq++; } @@ -3305,10 +3338,10 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog, seq = dumper->cur_seq; idx = dumper->cur_idx; while (l > size && seq < dumper->next_seq) { - struct printk_log *msg = log_from_idx(idx); + struct printk_log *msg = log_from_idx(log, idx); l -= msg_print_text(msg, true, NULL, 0); - idx = log_next(idx); + idx = log_next(log, idx); seq++; } @@ -3318,10 +3351,10 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog, l = 0; while (seq < dumper->next_seq) { - struct printk_log *msg = log_from_idx(idx); + struct printk_log *msg = log_from_idx(log, idx); l += msg_print_text(msg, syslog, buf + l, size - l); - idx = log_next(idx); + idx = log_next(log, idx); seq++; }