Skip to content

Commit 7d7a23a

Browse files
jognesspmladek
authored andcommitted
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 <[email protected]> Reviewed-by: Petr Mladek <[email protected]> Signed-off-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent cf5b020 commit 7d7a23a

File tree

1 file changed

+50
-8
lines changed

1 file changed

+50
-8
lines changed

kernel/printk/printk.c

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,21 @@ static u64 console_seq;
402402
static u64 exclusive_console_stop_seq;
403403
static unsigned long console_dropped;
404404

405-
/* the next printk record to read after the last 'clear' command */
406-
static u64 clear_seq;
405+
struct latched_seq {
406+
seqcount_latch_t latch;
407+
u64 val[2];
408+
};
409+
410+
/*
411+
* The next printk record to read after the last 'clear' command. There are
412+
* two copies (updated with seqcount_latch) so that reads can locklessly
413+
* access a valid value. Writers are synchronized by @logbuf_lock.
414+
*/
415+
static struct latched_seq clear_seq = {
416+
.latch = SEQCNT_LATCH_ZERO(clear_seq.latch),
417+
.val[0] = 0,
418+
.val[1] = 0,
419+
};
407420

408421
#ifdef CONFIG_PRINTK_CALLER
409422
#define PREFIX_MAX 48
@@ -457,6 +470,31 @@ bool printk_percpu_data_ready(void)
457470
return __printk_percpu_data_ready;
458471
}
459472

473+
/* Must be called under logbuf_lock. */
474+
static void latched_seq_write(struct latched_seq *ls, u64 val)
475+
{
476+
raw_write_seqcount_latch(&ls->latch);
477+
ls->val[0] = val;
478+
raw_write_seqcount_latch(&ls->latch);
479+
ls->val[1] = val;
480+
}
481+
482+
/* Can be called from any context. */
483+
static u64 latched_seq_read_nolock(struct latched_seq *ls)
484+
{
485+
unsigned int seq;
486+
unsigned int idx;
487+
u64 val;
488+
489+
do {
490+
seq = raw_read_seqcount_latch(&ls->latch);
491+
idx = seq & 0x1;
492+
val = ls->val[idx];
493+
} while (read_seqcount_latch_retry(&ls->latch, seq));
494+
495+
return val;
496+
}
497+
460498
/* Return log buffer address */
461499
char *log_buf_addr_get(void)
462500
{
@@ -801,7 +839,7 @@ static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
801839
* like issued by 'dmesg -c'. Reading /dev/kmsg itself
802840
* changes no global state, and does not clear anything.
803841
*/
804-
user->seq = clear_seq;
842+
user->seq = latched_seq_read_nolock(&clear_seq);
805843
break;
806844
case SEEK_END:
807845
/* after the last record */
@@ -960,6 +998,9 @@ void log_buf_vmcoreinfo_setup(void)
960998

961999
VMCOREINFO_SIZE(atomic_long_t);
9621000
VMCOREINFO_TYPE_OFFSET(atomic_long_t, counter);
1001+
1002+
VMCOREINFO_STRUCT_SIZE(latched_seq);
1003+
VMCOREINFO_OFFSET(latched_seq, val);
9631004
}
9641005
#endif
9651006

@@ -1557,7 +1598,8 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
15571598
* Find first record that fits, including all following records,
15581599
* into the user-provided buffer for this dump.
15591600
*/
1560-
seq = find_first_fitting_seq(clear_seq, -1, size, true, time);
1601+
seq = find_first_fitting_seq(latched_seq_read_nolock(&clear_seq), -1,
1602+
size, true, time);
15611603

15621604
prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX);
15631605

@@ -1584,7 +1626,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
15841626
}
15851627

15861628
if (clear)
1587-
clear_seq = seq;
1629+
latched_seq_write(&clear_seq, seq);
15881630
logbuf_unlock_irq();
15891631

15901632
kfree(text);
@@ -1594,7 +1636,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
15941636
static void syslog_clear(void)
15951637
{
15961638
logbuf_lock_irq();
1597-
clear_seq = prb_next_seq(prb);
1639+
latched_seq_write(&clear_seq, prb_next_seq(prb));
15981640
logbuf_unlock_irq();
15991641
}
16001642

@@ -3336,7 +3378,7 @@ void kmsg_dump(enum kmsg_dump_reason reason)
33363378
dumper->active = true;
33373379

33383380
logbuf_lock_irqsave(flags);
3339-
dumper->cur_seq = clear_seq;
3381+
dumper->cur_seq = latched_seq_read_nolock(&clear_seq);
33403382
dumper->next_seq = prb_next_seq(prb);
33413383
logbuf_unlock_irqrestore(flags);
33423384

@@ -3534,7 +3576,7 @@ EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
35343576
*/
35353577
void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
35363578
{
3537-
dumper->cur_seq = clear_seq;
3579+
dumper->cur_seq = latched_seq_read_nolock(&clear_seq);
35383580
dumper->next_seq = prb_next_seq(prb);
35393581
}
35403582

0 commit comments

Comments
 (0)