From a0d38d824d92325e893d09a8b9bd48e74172917e Mon Sep 17 00:00:00 2001 From: John Ogness Date: Mon, 30 Nov 2020 01:41:58 +0106 Subject: [PATCH 087/323] printk: use seqcount_latch for clear_seq Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.10/older/patches-5.10.215-rt107.tar.xz 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 Reviewed-by: Petr Mladek Signed-off-by: Sebastian Andrzej Siewior --- 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 6db3c574e4aa8..d40bc4665f5c0 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -404,8 +404,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 @@ -459,6 +472,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) { @@ -804,7 +842,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 */ @@ -963,6 +1001,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 @@ -1560,7 +1601,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); @@ -1587,7 +1629,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); @@ -1597,7 +1639,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(); } @@ -3370,7 +3412,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); @@ -3568,7 +3610,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.44.0