summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/0081-printk-remove-logbuf_lock-writer-protection-of-ringb.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches-rt/0081-printk-remove-logbuf_lock-writer-protection-of-ringb.patch')
-rw-r--r--debian/patches-rt/0081-printk-remove-logbuf_lock-writer-protection-of-ringb.patch251
1 files changed, 251 insertions, 0 deletions
diff --git a/debian/patches-rt/0081-printk-remove-logbuf_lock-writer-protection-of-ringb.patch b/debian/patches-rt/0081-printk-remove-logbuf_lock-writer-protection-of-ringb.patch
new file mode 100644
index 000000000..15ea7d1b6
--- /dev/null
+++ b/debian/patches-rt/0081-printk-remove-logbuf_lock-writer-protection-of-ringb.patch
@@ -0,0 +1,251 @@
+From c2d6c379547a29aa25202641659917ecd9ae7712 Mon Sep 17 00:00:00 2001
+From: John Ogness <john.ogness@linutronix.de>
+Date: Wed, 9 Dec 2020 01:50:53 +0106
+Subject: [PATCH 081/323] printk: remove logbuf_lock writer-protection of
+ ringbuffer
+Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.10/older/patches-5.10.204-rt100.tar.xz
+
+Since the ringbuffer is lockless, there is no need for it to be
+protected by @logbuf_lock. Remove @logbuf_lock writer-protection of
+the ringbuffer. The reader-protection is not removed because some
+variables, used by readers, are using @logbuf_lock for synchronization:
+@syslog_seq, @syslog_time, @syslog_partial, @console_seq,
+struct kmsg_dumper.
+
+For PRINTK_NMI_DIRECT_CONTEXT_MASK, @logbuf_lock usage is not removed
+because it may be used for dumper synchronization.
+
+Without @logbuf_lock synchronization of vprintk_store() it is no
+longer possible to use the single static buffer for temporarily
+sprint'ing the message. Instead, use vsnprintf() to determine the
+length and perform the real vscnprintf() using the area reserved from
+the ringbuffer. This leads to suboptimal packing of the message data,
+but will result in less wasted storage than multiple per-cpu buffers
+to support lockless temporary sprint'ing.
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+Reviewed-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+Reviewed-by: Petr Mladek <pmladek@suse.com>
+Signed-off-by: Petr Mladek <pmladek@suse.com>
+Link: https://lore.kernel.org/r/20201209004453.17720-3-john.ogness@linutronix.de
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ kernel/printk/printk.c | 138 +++++++++++++++++++++++++++++------------
+ 1 file changed, 98 insertions(+), 40 deletions(-)
+
+diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
+index 9a7c2b561e6d..b0a3f7827819 100644
+--- a/kernel/printk/printk.c
++++ b/kernel/printk/printk.c
+@@ -1129,7 +1129,7 @@ void __init setup_log_buf(int early)
+ new_descs, ilog2(new_descs_count),
+ new_infos);
+
+- logbuf_lock_irqsave(flags);
++ printk_safe_enter_irqsave(flags);
+
+ log_buf_len = new_log_buf_len;
+ log_buf = new_log_buf;
+@@ -1146,7 +1146,7 @@ void __init setup_log_buf(int early)
+ */
+ prb = &printk_rb_dynamic;
+
+- logbuf_unlock_irqrestore(flags);
++ printk_safe_exit_irqrestore(flags);
+
+ if (seq != prb_next_seq(&printk_rb_static)) {
+ pr_err("dropped %llu messages\n",
+@@ -1887,18 +1887,90 @@ static inline u32 printk_caller_id(void)
+ 0x80000000 + raw_smp_processor_id();
+ }
+
+-/* Must be called under logbuf_lock. */
++/**
++ * parse_prefix - Parse level and control flags.
++ *
++ * @text: The terminated text message.
++ * @level: A pointer to the current level value, will be updated.
++ * @lflags: A pointer to the current log flags, will be updated.
++ *
++ * @level may be NULL if the caller is not interested in the parsed value.
++ * Otherwise the variable pointed to by @level must be set to
++ * LOGLEVEL_DEFAULT in order to be updated with the parsed value.
++ *
++ * @lflags may be NULL if the caller is not interested in the parsed value.
++ * Otherwise the variable pointed to by @lflags will be OR'd with the parsed
++ * value.
++ *
++ * Return: The length of the parsed level and control flags.
++ */
++static u16 parse_prefix(char *text, int *level, enum log_flags *lflags)
++{
++ u16 prefix_len = 0;
++ int kern_level;
++
++ while (*text) {
++ kern_level = printk_get_level(text);
++ if (!kern_level)
++ break;
++
++ switch (kern_level) {
++ case '0' ... '7':
++ if (level && *level == LOGLEVEL_DEFAULT)
++ *level = kern_level - '0';
++ break;
++ case 'c': /* KERN_CONT */
++ if (lflags)
++ *lflags |= LOG_CONT;
++ }
++
++ prefix_len += 2;
++ text += 2;
++ }
++
++ return prefix_len;
++}
++
++static u16 printk_sprint(char *text, u16 size, int facility, enum log_flags *lflags,
++ const char *fmt, va_list args)
++{
++ u16 text_len;
++
++ text_len = vscnprintf(text, size, fmt, args);
++
++ /* Mark and strip a trailing newline. */
++ if (text_len && text[text_len - 1] == '\n') {
++ text_len--;
++ *lflags |= LOG_NEWLINE;
++ }
++
++ /* Strip log level and control flags. */
++ if (facility == 0) {
++ u16 prefix_len;
++
++ prefix_len = parse_prefix(text, NULL, NULL);
++ if (prefix_len) {
++ text_len -= prefix_len;
++ memmove(text, text + prefix_len, text_len);
++ }
++ }
++
++ return text_len;
++}
++
++__printf(4, 0)
+ int vprintk_store(int facility, int level,
+ const struct dev_printk_info *dev_info,
+ const char *fmt, va_list args)
+ {
+ const u32 caller_id = printk_caller_id();
+- static char textbuf[LOG_LINE_MAX];
+ struct prb_reserved_entry e;
+ enum log_flags lflags = 0;
+ struct printk_record r;
+ u16 trunc_msg_len = 0;
+- char *text = textbuf;
++ char prefix_buf[8];
++ u16 reserve_size;
++ va_list args2;
+ u16 text_len;
+ u64 ts_nsec;
+
+@@ -1911,35 +1983,21 @@ int vprintk_store(int facility, int level,
+ ts_nsec = local_clock();
+
+ /*
+- * The printf needs to come first; we need the syslog
+- * prefix which might be passed-in as a parameter.
++ * The sprintf needs to come first since the syslog prefix might be
++ * passed in as a parameter. An extra byte must be reserved so that
++ * later the vscnprintf() into the reserved buffer has room for the
++ * terminating '\0', which is not counted by vsnprintf().
+ */
+- text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
+-
+- /* mark and strip a trailing newline */
+- if (text_len && text[text_len-1] == '\n') {
+- text_len--;
+- lflags |= LOG_NEWLINE;
+- }
+-
+- /* strip kernel syslog prefix and extract log level or control flags */
+- if (facility == 0) {
+- int kern_level;
++ va_copy(args2, args);
++ reserve_size = vsnprintf(&prefix_buf[0], sizeof(prefix_buf), fmt, args2) + 1;
++ va_end(args2);
+
+- while ((kern_level = printk_get_level(text)) != 0) {
+- switch (kern_level) {
+- case '0' ... '7':
+- if (level == LOGLEVEL_DEFAULT)
+- level = kern_level - '0';
+- break;
+- case 'c': /* KERN_CONT */
+- lflags |= LOG_CONT;
+- }
++ if (reserve_size > LOG_LINE_MAX)
++ reserve_size = LOG_LINE_MAX;
+
+- text_len -= 2;
+- text += 2;
+- }
+- }
++ /* Extract log level or control flags. */
++ if (facility == 0)
++ parse_prefix(&prefix_buf[0], &level, &lflags);
+
+ if (level == LOGLEVEL_DEFAULT)
+ level = default_message_loglevel;
+@@ -1948,9 +2006,10 @@ int vprintk_store(int facility, int level,
+ lflags |= LOG_NEWLINE;
+
+ if (lflags & LOG_CONT) {
+- prb_rec_init_wr(&r, text_len);
++ prb_rec_init_wr(&r, reserve_size);
+ if (prb_reserve_in_last(&e, prb, &r, caller_id, LOG_LINE_MAX)) {
+- memcpy(&r.text_buf[r.info->text_len], text, text_len);
++ text_len = printk_sprint(&r.text_buf[r.info->text_len], reserve_size,
++ facility, &lflags, fmt, args);
+ r.info->text_len += text_len;
+
+ if (lflags & LOG_NEWLINE) {
+@@ -1969,18 +2028,18 @@ int vprintk_store(int facility, int level,
+ * prb_reserve_in_last() and prb_reserve() purposely invalidate the
+ * structure when they fail.
+ */
+- prb_rec_init_wr(&r, text_len);
++ prb_rec_init_wr(&r, reserve_size);
+ if (!prb_reserve(&e, prb, &r)) {
+ /* truncate the message if it is too long for empty buffer */
+- truncate_msg(&text_len, &trunc_msg_len);
++ truncate_msg(&reserve_size, &trunc_msg_len);
+
+- prb_rec_init_wr(&r, text_len + trunc_msg_len);
++ prb_rec_init_wr(&r, reserve_size + trunc_msg_len);
+ if (!prb_reserve(&e, prb, &r))
+ return 0;
+ }
+
+ /* fill message */
+- memcpy(&r.text_buf[0], text, text_len);
++ text_len = printk_sprint(&r.text_buf[0], reserve_size, facility, &lflags, fmt, args);
+ if (trunc_msg_len)
+ memcpy(&r.text_buf[text_len], trunc_msg, trunc_msg_len);
+ r.info->text_len = text_len + trunc_msg_len;
+@@ -2021,10 +2080,9 @@ asmlinkage int vprintk_emit(int facility, int level,
+ boot_delay_msec(level);
+ printk_delay();
+
+- /* This stops the holder of console_sem just where we want him */
+- logbuf_lock_irqsave(flags);
++ printk_safe_enter_irqsave(flags);
+ printed_len = vprintk_store(facility, level, dev_info, fmt, args);
+- logbuf_unlock_irqrestore(flags);
++ printk_safe_exit_irqrestore(flags);
+
+ /* If called from the scheduler, we can not call up(). */
+ if (!in_sched) {
+--
+2.43.0
+