[vz8] ve/printk: Fix printk virtualization

Submitted by Andrey Ryabinin on July 20, 2020, 11:37 a.m.

Details

Message ID 20200720113743.24533-1-aryabinin@virtuozzo.com
State New
Series "ve/printk: Fix printk virtualization"
Headers show

Commit Message

Andrey Ryabinin July 20, 2020, 11:37 a.m.
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 <aryabinin@virtuozzo.com>
---
 kernel/printk/printk.c | 99 ++++++++++++++++++++++++++++--------------
 1 file changed, 66 insertions(+), 33 deletions(-)

Patch hide | download patch | download mbox

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++;
 	}