aboutsummaryrefslogtreecommitdiffstats
path: root/features/rt/printk-use-seqcount_latch-for-clear_seq.patch
diff options
context:
space:
mode:
Diffstat (limited to 'features/rt/printk-use-seqcount_latch-for-clear_seq.patch')
-rw-r--r--features/rt/printk-use-seqcount_latch-for-clear_seq.patch146
1 files changed, 146 insertions, 0 deletions
diff --git a/features/rt/printk-use-seqcount_latch-for-clear_seq.patch b/features/rt/printk-use-seqcount_latch-for-clear_seq.patch
new file mode 100644
index 00000000..35a9bd69
--- /dev/null
+++ b/features/rt/printk-use-seqcount_latch-for-clear_seq.patch
@@ -0,0 +1,146 @@
+From e6a23cb5f39c0c3e2cbaef3af7b0ab41fd2104ae Mon Sep 17 00:00:00 2001
+From: John Ogness <john.ogness@linutronix.de>
+Date: Mon, 30 Nov 2020 01:41:58 +0106
+Subject: [PATCH 012/191] printk: use seqcount_latch for clear_seq
+
+kmsg_dump_rewind_nolock() locklessly reads @clear_seq. However,
+this is not done atomically. Since @clear_seq is 64-bit, this
+cannot be an atomic operation for all platforms. Therefore, use
+a seqcount_latch to allow readers to always read a consistent
+value.
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+Reviewed-by: Petr Mladek <pmladek@suse.com>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ kernel/printk/printk.c | 58 ++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 50 insertions(+), 8 deletions(-)
+
+diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
+index 8a903faaec4e..b764222b325b 100644
+--- a/kernel/printk/printk.c
++++ b/kernel/printk/printk.c
+@@ -402,8 +402,21 @@ static u64 console_seq;
+ static u64 exclusive_console_stop_seq;
+ static unsigned long console_dropped;
+
+-/* the next printk record to read after the last 'clear' command */
+-static u64 clear_seq;
++struct latched_seq {
++ seqcount_latch_t latch;
++ u64 val[2];
++};
++
++/*
++ * The next printk record to read after the last 'clear' command. There are
++ * two copies (updated with seqcount_latch) so that reads can locklessly
++ * access a valid value. Writers are synchronized by @logbuf_lock.
++ */
++static struct latched_seq clear_seq = {
++ .latch = SEQCNT_LATCH_ZERO(clear_seq.latch),
++ .val[0] = 0,
++ .val[1] = 0,
++};
+
+ #ifdef CONFIG_PRINTK_CALLER
+ #define PREFIX_MAX 48
+@@ -457,6 +470,31 @@ bool printk_percpu_data_ready(void)
+ return __printk_percpu_data_ready;
+ }
+
++/* Must be called under logbuf_lock. */
++static void latched_seq_write(struct latched_seq *ls, u64 val)
++{
++ raw_write_seqcount_latch(&ls->latch);
++ ls->val[0] = val;
++ raw_write_seqcount_latch(&ls->latch);
++ ls->val[1] = val;
++}
++
++/* Can be called from any context. */
++static u64 latched_seq_read_nolock(struct latched_seq *ls)
++{
++ unsigned int seq;
++ unsigned int idx;
++ u64 val;
++
++ do {
++ seq = raw_read_seqcount_latch(&ls->latch);
++ idx = seq & 0x1;
++ val = ls->val[idx];
++ } while (read_seqcount_latch_retry(&ls->latch, seq));
++
++ return val;
++}
++
+ /* Return log buffer address */
+ char *log_buf_addr_get(void)
+ {
+@@ -801,7 +839,7 @@ static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
+ * like issued by 'dmesg -c'. Reading /dev/kmsg itself
+ * changes no global state, and does not clear anything.
+ */
+- user->seq = clear_seq;
++ user->seq = latched_seq_read_nolock(&clear_seq);
+ break;
+ case SEEK_END:
+ /* after the last record */
+@@ -960,6 +998,9 @@ void log_buf_vmcoreinfo_setup(void)
+
+ VMCOREINFO_SIZE(atomic_long_t);
+ VMCOREINFO_TYPE_OFFSET(atomic_long_t, counter);
++
++ VMCOREINFO_STRUCT_SIZE(latched_seq);
++ VMCOREINFO_OFFSET(latched_seq, val);
+ }
+ #endif
+
+@@ -1557,7 +1598,8 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
+ * Find first record that fits, including all following records,
+ * into the user-provided buffer for this dump.
+ */
+- seq = find_first_fitting_seq(clear_seq, -1, size, true, time);
++ seq = find_first_fitting_seq(latched_seq_read_nolock(&clear_seq), -1,
++ size, true, time);
+
+ prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX);
+
+@@ -1584,7 +1626,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
+ }
+
+ if (clear)
+- clear_seq = seq;
++ latched_seq_write(&clear_seq, seq);
+ logbuf_unlock_irq();
+
+ kfree(text);
+@@ -1594,7 +1636,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
+ static void syslog_clear(void)
+ {
+ logbuf_lock_irq();
+- clear_seq = prb_next_seq(prb);
++ latched_seq_write(&clear_seq, prb_next_seq(prb));
+ logbuf_unlock_irq();
+ }
+
+@@ -3336,7 +3378,7 @@ void kmsg_dump(enum kmsg_dump_reason reason)
+ dumper->active = true;
+
+ logbuf_lock_irqsave(flags);
+- dumper->cur_seq = clear_seq;
++ dumper->cur_seq = latched_seq_read_nolock(&clear_seq);
+ dumper->next_seq = prb_next_seq(prb);
+ logbuf_unlock_irqrestore(flags);
+
+@@ -3534,7 +3576,7 @@ EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
+ */
+ void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
+ {
+- dumper->cur_seq = clear_seq;
++ dumper->cur_seq = latched_seq_read_nolock(&clear_seq);
+ dumper->next_seq = prb_next_seq(prb);
+ }
+
+--
+2.19.1
+