Skip to content

Commit f244b4d

Browse files
committed
printk: ringbuffer: Improve prb_next_seq() performance
prb_next_seq() always iterates from the first known sequence number. In the worst case, it might loop 8k times for 256kB buffer, 15k times for 512kB buffer, and 64k times for 2MB buffer. It was reported that polling and reading using syslog interface might occupy 50% of CPU. Speedup the search by storing @id of the last finalized descriptor. The loop is still needed because the @id is stored and read in the best effort way. An atomic variable is used to keep the @id consistent. But the stores and reads are not serialized against each other. The descriptor could get reused in the meantime. The related sequence number will be used only when it is still valid. An invalid value should be read _only_ when there is a flood of messages and the ringbuffer is rapidly reused. The performance is the least problem in this case. Reported-by: Chunlei Wang <[email protected]> Signed-off-by: Mukesh Ojha <[email protected]> Reviewed-by: John Ogness <[email protected]> Signed-off-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/r/[email protected] Link: https://lore.kernel.org/lkml/YXlddJxLh77DKfIO@alley/T/#m43062e8b2a17f8dbc8c6ccdb8851fb0dbaabbb14
1 parent 11e4b63 commit f244b4d

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

kernel/printk/printk_ringbuffer.c

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,10 @@ static enum desc_state desc_read(struct prb_desc_ring *desc_ring,
474474
* state has been re-checked. A memcpy() for all of @desc
475475
* cannot be used because of the atomic_t @state_var field.
476476
*/
477-
memcpy(&desc_out->text_blk_lpos, &desc->text_blk_lpos,
478-
sizeof(desc_out->text_blk_lpos)); /* LMM(desc_read:C) */
477+
if (desc_out) {
478+
memcpy(&desc_out->text_blk_lpos, &desc->text_blk_lpos,
479+
sizeof(desc_out->text_blk_lpos)); /* LMM(desc_read:C) */
480+
}
479481
if (seq_out)
480482
*seq_out = info->seq; /* also part of desc_read:C */
481483
if (caller_id_out)
@@ -528,7 +530,8 @@ static enum desc_state desc_read(struct prb_desc_ring *desc_ring,
528530
state_val = atomic_long_read(state_var); /* LMM(desc_read:E) */
529531
d_state = get_desc_state(id, state_val);
530532
out:
531-
atomic_long_set(&desc_out->state_var, state_val);
533+
if (desc_out)
534+
atomic_long_set(&desc_out->state_var, state_val);
532535
return d_state;
533536
}
534537

@@ -1449,6 +1452,9 @@ static void desc_make_final(struct prb_desc_ring *desc_ring, unsigned long id)
14491452

14501453
atomic_long_cmpxchg_relaxed(&d->state_var, prev_state_val,
14511454
DESC_SV(id, desc_finalized)); /* LMM(desc_make_final:A) */
1455+
1456+
/* Best effort to remember the last finalized @id. */
1457+
atomic_long_set(&desc_ring->last_finalized_id, id);
14521458
}
14531459

14541460
/**
@@ -1657,7 +1663,12 @@ void prb_commit(struct prb_reserved_entry *e)
16571663
*/
16581664
void prb_final_commit(struct prb_reserved_entry *e)
16591665
{
1666+
struct prb_desc_ring *desc_ring = &e->rb->desc_ring;
1667+
16601668
_prb_commit(e, desc_finalized);
1669+
1670+
/* Best effort to remember the last finalized @id. */
1671+
atomic_long_set(&desc_ring->last_finalized_id, e->id);
16611672
}
16621673

16631674
/*
@@ -2005,9 +2016,39 @@ u64 prb_first_valid_seq(struct printk_ringbuffer *rb)
20052016
*/
20062017
u64 prb_next_seq(struct printk_ringbuffer *rb)
20072018
{
2008-
u64 seq = 0;
2019+
struct prb_desc_ring *desc_ring = &rb->desc_ring;
2020+
enum desc_state d_state;
2021+
unsigned long id;
2022+
u64 seq;
2023+
2024+
/* Check if the cached @id still points to a valid @seq. */
2025+
id = atomic_long_read(&desc_ring->last_finalized_id);
2026+
d_state = desc_read(desc_ring, id, NULL, &seq, NULL);
20092027

2010-
/* Search forward from the oldest descriptor. */
2028+
if (d_state == desc_finalized || d_state == desc_reusable) {
2029+
/*
2030+
* Begin searching after the last finalized record.
2031+
*
2032+
* On 0, the search must begin at 0 because of hack#2
2033+
* of the bootstrapping phase it is not known if a
2034+
* record at index 0 exists.
2035+
*/
2036+
if (seq != 0)
2037+
seq++;
2038+
} else {
2039+
/*
2040+
* The information about the last finalized sequence number
2041+
* has gone. It should happen only when there is a flood of
2042+
* new messages and the ringbuffer is rapidly recycled.
2043+
* Give up and start from the beginning.
2044+
*/
2045+
seq = 0;
2046+
}
2047+
2048+
/*
2049+
* The information about the last finalized @seq might be inaccurate.
2050+
* Search forward to find the current one.
2051+
*/
20112052
while (_prb_read_valid(rb, &seq, NULL, NULL))
20122053
seq++;
20132054

@@ -2044,6 +2085,7 @@ void prb_init(struct printk_ringbuffer *rb,
20442085
rb->desc_ring.infos = infos;
20452086
atomic_long_set(&rb->desc_ring.head_id, DESC0_ID(descbits));
20462087
atomic_long_set(&rb->desc_ring.tail_id, DESC0_ID(descbits));
2088+
atomic_long_set(&rb->desc_ring.last_finalized_id, DESC0_ID(descbits));
20472089

20482090
rb->text_data_ring.size_bits = textbits;
20492091
rb->text_data_ring.data = text_buf;

kernel/printk/printk_ringbuffer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct prb_desc_ring {
7575
struct printk_info *infos;
7676
atomic_long_t head_id;
7777
atomic_long_t tail_id;
78+
atomic_long_t last_finalized_id;
7879
};
7980

8081
/*
@@ -258,6 +259,7 @@ static struct printk_ringbuffer name = { \
258259
.infos = &_##name##_infos[0], \
259260
.head_id = ATOMIC_INIT(DESC0_ID(descbits)), \
260261
.tail_id = ATOMIC_INIT(DESC0_ID(descbits)), \
262+
.last_finalized_id = ATOMIC_INIT(DESC0_ID(descbits)), \
261263
}, \
262264
.text_data_ring = { \
263265
.size_bits = (avgtextbits) + (descbits), \

0 commit comments

Comments
 (0)