Re: [RFC PATCH v4 9/9] printk: use a new ringbuffer implementation

From: Dave Young
Date: Fri Aug 16 2019 - 01:49:01 EST


John, can you cc kexec list for your later series?

On 08/08/19 at 12:32am, John Ogness wrote:
> This is a major change because the API (and underlying workings)
> of the new ringbuffer are completely different than the previous
> ringbuffer. Since there are several components of the printk
> infrastructure that use the ringbuffer API (console, /dev/kmsg,
> syslog, kmsg_dump), there are quite a few changes throughout the
> printk implementation.
>
> This is also a conservative change because it continues to use the
> logbuf_lock raw spinlock even though the new ringbuffer is lockless.
>
> The externally visible changes are:
>
> 1. The exported vmcore info has changed:
>
> - VMCOREINFO_SYMBOL(log_buf);
> - VMCOREINFO_SYMBOL(log_buf_len);
> - VMCOREINFO_SYMBOL(log_first_idx);
> - VMCOREINFO_SYMBOL(clear_idx);
> - VMCOREINFO_SYMBOL(log_next_idx);
> + VMCOREINFO_SYMBOL(printk_rb_static);
> + VMCOREINFO_SYMBOL(printk_rb_dynamic);

I assumed this needs some userspace work in kexec, how did you test
them?

makedumpfile should need changes to dump the kernel log.

Also kexec-tools includes a vmcore-dmesg.c to extrace dmesg from
/proc/vmcore.

>
> 2. For the CONFIG_PPC_POWERNV powerpc platform, kernel log buffer
> registration is no longer available because there is no longer
> a single contigous block of memory to represent all of the
> ringbuffer.
>
> Signed-off-by: John Ogness <john.ogness@xxxxxxxxxxxxx>
> ---
> arch/powerpc/platforms/powernv/opal.c | 22 +-
> include/linux/kmsg_dump.h | 6 +-
> include/linux/printk.h | 12 -
> kernel/printk/printk.c | 745 ++++++++++++++------------
> kernel/printk/ringbuffer.h | 24 +
> 5 files changed, 415 insertions(+), 394 deletions(-)
>
> diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
> index aba443be7daa..8c4b894b6663 100644
> --- a/arch/powerpc/platforms/powernv/opal.c
> +++ b/arch/powerpc/platforms/powernv/opal.c
> @@ -806,30 +806,10 @@ static void opal_export_attrs(void)
>
> static void __init opal_dump_region_init(void)
> {
> - void *addr;
> - uint64_t size;
> - int rc;
> -
> if (!opal_check_token(OPAL_REGISTER_DUMP_REGION))
> return;
>
> - /* Register kernel log buffer */
> - addr = log_buf_addr_get();
> - if (addr == NULL)
> - return;
> -
> - size = log_buf_len_get();
> - if (size == 0)
> - return;
> -
> - rc = opal_register_dump_region(OPAL_DUMP_REGION_LOG_BUF,
> - __pa(addr), size);
> - /* Don't warn if this is just an older OPAL that doesn't
> - * know about that call
> - */
> - if (rc && rc != OPAL_UNSUPPORTED)
> - pr_warn("DUMP: Failed to register kernel log buffer. "
> - "rc = %d\n", rc);
> + pr_warn("DUMP: This kernel does not support kernel log buffer registration.\n");
> }
>
> static void opal_pdev_init(const char *compatible)
> diff --git a/include/linux/kmsg_dump.h b/include/linux/kmsg_dump.h
> index 2e7a1e032c71..d9b721347742 100644
> --- a/include/linux/kmsg_dump.h
> +++ b/include/linux/kmsg_dump.h
> @@ -46,10 +46,8 @@ struct kmsg_dumper {
> bool registered;
>
> /* private state of the kmsg iterator */
> - u32 cur_idx;
> - u32 next_idx;
> - u64 cur_seq;
> - u64 next_seq;
> + u64 last_seq;
> + u64 until_seq;
> };
>
> #ifdef CONFIG_PRINTK
> diff --git a/include/linux/printk.h b/include/linux/printk.h
> index cefd374c47b1..fd3007659cfb 100644
> --- a/include/linux/printk.h
> +++ b/include/linux/printk.h
> @@ -194,8 +194,6 @@ devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, void __user *buf,
>
> extern void wake_up_klogd(void);
>
> -char *log_buf_addr_get(void);
> -u32 log_buf_len_get(void);
> void log_buf_vmcoreinfo_setup(void);
> void __init setup_log_buf(int early);
> __printf(1, 2) void dump_stack_set_arch_desc(const char *fmt, ...);
> @@ -235,16 +233,6 @@ static inline void wake_up_klogd(void)
> {
> }
>
> -static inline char *log_buf_addr_get(void)
> -{
> - return NULL;
> -}
> -
> -static inline u32 log_buf_len_get(void)
> -{
> - return 0;
> -}
> -
> static inline void log_buf_vmcoreinfo_setup(void)
> {
> }
> diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
> index 1888f6a3b694..1a50e0c43775 100644
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -56,6 +56,7 @@
> #define CREATE_TRACE_POINTS
> #include <trace/events/printk.h>
>
> +#include "ringbuffer.h"
> #include "console_cmdline.h"
> #include "braille.h"
> #include "internal.h"
> @@ -409,28 +410,38 @@ DEFINE_RAW_SPINLOCK(logbuf_lock);
>
> #ifdef CONFIG_PRINTK
> DECLARE_WAIT_QUEUE_HEAD(log_wait);
> -/* the next printk record to read by syslog(READ) or /proc/kmsg */
> -static u64 syslog_seq;
> -static u32 syslog_idx;
> -static size_t syslog_partial;
> -static bool syslog_time;
>
> -/* index and sequence number of the first record stored in the buffer */
> -static u64 log_first_seq;
> -static u32 log_first_idx;
> +/*
> + * Define the average message size. This only affects the number of
> + * descriptors that will be available. Underestimating is better than
> + * overestimating (too many available descriptors is better than not enough).
> + */
> +#define PRB_AVGBITS 6
> +
> +DECLARE_PRINTKRB(printk_rb_static, PRB_AVGBITS,
> + CONFIG_LOG_BUF_SHIFT - PRB_AVGBITS, &log_wait);
> +
> +static struct printk_ringbuffer printk_rb_dynamic;
>
> -/* index and sequence number of the next record to store in the buffer */
> -static u64 log_next_seq;
> -static u32 log_next_idx;
> +static struct printk_ringbuffer *prb = &printk_rb_static;
>
> -/* the next printk record to write to the console */
> -static u64 console_seq;
> -static u32 console_idx;
> +/* the last printk record read by syslog(READ) or /proc/kmsg */
> +static u64 syslog_last_seq;
> +DECLARE_PRINTKRB_ENTRY(syslog_entry,
> + sizeof(struct printk_log) + CONSOLE_EXT_LOG_MAX);
> +DECLARE_PRINTKRB_ITER(syslog_iter, &printk_rb_static, &syslog_entry);
> +static size_t syslog_partial;
> +static bool syslog_time;
> +
> +/* the last printk record written to the console */
> +static u64 console_last_seq;
> +DECLARE_PRINTKRB_ENTRY(console_entry,
> + sizeof(struct printk_log) + CONSOLE_EXT_LOG_MAX);
> +DECLARE_PRINTKRB_ITER(console_iter, &printk_rb_static, &console_entry);
> static u64 exclusive_console_stop_seq;
>
> -/* the next printk record to read after the last 'clear' command */
> -static u64 clear_seq;
> -static u32 clear_idx;
> +/* the last printk record read before the last 'clear' command */
> +static u64 clear_last_seq;
>
> #ifdef CONFIG_PRINTK_CALLER
> #define PREFIX_MAX 48
> @@ -446,22 +457,8 @@ static u32 clear_idx;
> #define LOG_ALIGN __alignof__(struct printk_log)
> #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
> #define LOG_BUF_LEN_MAX (u32)(1 << 31)
> -static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
> -static char *log_buf = __log_buf;
> static u32 log_buf_len = __LOG_BUF_LEN;
>
> -/* Return log buffer address */
> -char *log_buf_addr_get(void)
> -{
> - return log_buf;
> -}
> -
> -/* Return log buffer size */
> -u32 log_buf_len_get(void)
> -{
> - return log_buf_len;
> -}
> -
> /* human readable text of the record */
> static char *log_text(const struct printk_log *msg)
> {
> @@ -474,92 +471,12 @@ static char *log_dict(const struct printk_log *msg)
> return (char *)msg + sizeof(struct printk_log) + msg->text_len;
> }
>
> -/* get record by index; idx must point to valid msg */
> -static struct printk_log *log_from_idx(u32 idx)
> -{
> - struct printk_log *msg = (struct printk_log *)(log_buf + idx);
> -
> - /*
> - * A length == 0 record is the end of buffer marker. Wrap around and
> - * read the message at the start of the buffer.
> - */
> - if (!msg->len)
> - return (struct printk_log *)log_buf;
> - return msg;
> -}
> -
> -/* get next record; idx must point to valid msg */
> -static u32 log_next(u32 idx)
> -{
> - struct printk_log *msg = (struct printk_log *)(log_buf + idx);
> -
> - /* length == 0 indicates the end of the buffer; wrap */
> - /*
> - * A length == 0 record is the end of buffer marker. Wrap around and
> - * read the message at the start of the buffer as *this* one, and
> - * return the one after that.
> - */
> - if (!msg->len) {
> - msg = (struct printk_log *)log_buf;
> - return msg->len;
> - }
> - return idx + msg->len;
> -}
> -
> -/*
> - * Check whether there is enough free space for the given message.
> - *
> - * The same values of first_idx and next_idx mean that the buffer
> - * is either empty or full.
> - *
> - * If the buffer is empty, we must respect the position of the indexes.
> - * They cannot be reset to the beginning of the buffer.
> - */
> -static int logbuf_has_space(u32 msg_size, bool empty)
> -{
> - u32 free;
> -
> - if (log_next_idx > log_first_idx || empty)
> - free = max(log_buf_len - log_next_idx, log_first_idx);
> - else
> - free = log_first_idx - log_next_idx;
> -
> - /*
> - * We need space also for an empty header that signalizes wrapping
> - * of the buffer.
> - */
> - return free >= msg_size + sizeof(struct printk_log);
> -}
> -
> -static int log_make_free_space(u32 msg_size)
> -{
> - while (log_first_seq < log_next_seq &&
> - !logbuf_has_space(msg_size, false)) {
> - /* drop old messages until we have enough contiguous space */
> - log_first_idx = log_next(log_first_idx);
> - log_first_seq++;
> - }
> -
> - if (clear_seq < log_first_seq) {
> - clear_seq = log_first_seq;
> - clear_idx = log_first_idx;
> - }
> -
> - /* sequence numbers are equal, so the log buffer is empty */
> - if (logbuf_has_space(msg_size, log_first_seq == log_next_seq))
> - return 0;
> -
> - return -ENOMEM;
> -}
> -
> -/* compute the message size including the padding bytes */
> -static u32 msg_used_size(u16 text_len, u16 dict_len, u32 *pad_len)
> +/* compute the message size */
> +static u32 msg_used_size(u16 text_len, u16 dict_len)
> {
> u32 size;
>
> size = sizeof(struct printk_log) + text_len + dict_len;
> - *pad_len = (-size) & (LOG_ALIGN - 1);
> - size += *pad_len;
>
> return size;
> }
> @@ -573,21 +490,39 @@ static u32 msg_used_size(u16 text_len, u16 dict_len, u32 *pad_len)
> static const char trunc_msg[] = "<truncated>";
>
> static u32 truncate_msg(u16 *text_len, u16 *trunc_msg_len,
> - u16 *dict_len, u32 *pad_len)
> + u16 *dict_len)
> {
> /*
> * The message should not take the whole buffer. Otherwise, it might
> * get removed too soon.
> */
> u32 max_text_len = log_buf_len / MAX_LOG_TAKE_PART;
> + unsigned long max_available;
> +
> + /* determine available text space in the ringbuffer */
> + max_available = prb_unused(prb);
> + if (max_available <= sizeof(struct printk_log))
> + return 0;
> + max_available -= sizeof(struct printk_log);
> +
> + if (max_available < max_text_len)
> + max_text_len = max_available;
> +
> if (*text_len > max_text_len)
> *text_len = max_text_len;
> - /* enable the warning message */
> +
> + /* enable the warning message (if there is room) */
> *trunc_msg_len = strlen(trunc_msg);
> + if (*text_len >= *trunc_msg_len)
> + *text_len -= *trunc_msg_len;
> + else
> + *trunc_msg_len = 0;
> +
> /* disable the "dict" completely */
> *dict_len = 0;
> +
> /* compute the size again, count also the warning message */
> - return msg_used_size(*text_len + *trunc_msg_len, 0, pad_len);
> + return msg_used_size(*text_len + *trunc_msg_len, 0);
> }
>
> /* insert record into the buffer, discard old ones, update heads */
> @@ -596,34 +531,26 @@ static int log_store(u32 caller_id, int facility, int level,
> const char *dict, u16 dict_len,
> const char *text, u16 text_len)
> {
> + struct prb_reserved_entry res_entry;
> struct printk_log *msg;
> - u32 size, pad_len;
> u16 trunc_msg_len = 0;
> + char *rbuf;
> + u32 size;
>
> - /* number of '\0' padding bytes to next message */
> - size = msg_used_size(text_len, dict_len, &pad_len);
> + size = msg_used_size(text_len, dict_len);
>
> - if (log_make_free_space(size)) {
> + rbuf = prb_reserve(&res_entry, prb, size);
> + if (IS_ERR(rbuf)) {
> /* truncate the message if it is too long for empty buffer */
> - size = truncate_msg(&text_len, &trunc_msg_len,
> - &dict_len, &pad_len);
> + size = truncate_msg(&text_len, &trunc_msg_len, &dict_len);
> /* survive when the log buffer is too small for trunc_msg */
> - if (log_make_free_space(size))
> + rbuf = prb_reserve(&res_entry, prb, size);
> + if (IS_ERR(rbuf))
> return 0;
> }
>
> - if (log_next_idx + size + sizeof(struct printk_log) > log_buf_len) {
> - /*
> - * This message + an additional empty header does not fit
> - * at the end of the buffer. Add an empty header with len == 0
> - * to signify a wrap around.
> - */
> - memset(log_buf + log_next_idx, 0, sizeof(struct printk_log));
> - log_next_idx = 0;
> - }
> -
> /* fill message */
> - msg = (struct printk_log *)(log_buf + log_next_idx);
> + msg = (struct printk_log *)rbuf;
> memcpy(log_text(msg), text, text_len);
> msg->text_len = text_len;
> if (trunc_msg_len) {
> @@ -642,14 +569,13 @@ static int log_store(u32 caller_id, int facility, int level,
> #ifdef CONFIG_PRINTK_CALLER
> msg->caller_id = caller_id;
> #endif
> - memset(log_dict(msg) + dict_len, 0, pad_len);
> msg->len = size;
>
> /* insert message */
> - log_next_idx += msg->len;
> - log_next_seq++;
> + prb_commit(&res_entry);
>
> - return msg->text_len;
> + /* msg is no longer valid, return the local copy */
> + return text_len;
> }
>
> int dmesg_restrict = IS_ENABLED(CONFIG_SECURITY_DMESG_RESTRICT);
> @@ -770,13 +696,18 @@ static ssize_t msg_print_ext_body(char *buf, size_t size,
> return p - buf;
> }
>
> +#define PRINTK_RECORD_MAX (sizeof(struct printk_log) + \
> + CONSOLE_EXT_LOG_MAX + LOG_LINE_MAX + PREFIX_MAX)
> +
> /* /dev/kmsg - userspace message inject/listen interface */
> struct devkmsg_user {
> - u64 seq;
> - u32 idx;
> + u64 last_seq;
> + struct prb_iterator iter;
> struct ratelimit_state rs;
> struct mutex lock;
> char buf[CONSOLE_EXT_LOG_MAX];
> + struct prb_entry entry;
> + char msgbuf[PRINTK_RECORD_MAX];
> };
>
> static __printf(3, 4) __cold
> @@ -859,6 +790,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
> size_t count, loff_t *ppos)
> {
> struct devkmsg_user *user = file->private_data;
> + struct prb_iterator backup_iter;
> struct printk_log *msg;
> size_t len;
> ssize_t ret;
> @@ -871,7 +803,11 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
> return ret;
>
> logbuf_lock_irq();
> - while (user->seq == log_next_seq) {
> +
> + /* make a backup copy in case there is a problem */
> + prb_iter_copy(&backup_iter, &user->iter);
> +
> + if (prb_iter_next_valid_entry(&user->iter) == 0) {
> if (file->f_flags & O_NONBLOCK) {
> ret = -EAGAIN;
> logbuf_unlock_irq();
> @@ -879,43 +815,53 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
> }
>
> logbuf_unlock_irq();
> - ret = wait_event_interruptible(log_wait,
> - user->seq != log_next_seq);
> - if (ret)
> + ret = prb_iter_wait_next_valid_entry(&user->iter);
> + if (ret < 0)
> goto out;
> logbuf_lock_irq();
> }
>
> - if (user->seq < log_first_seq) {
> - /* our last seen message is gone, return error and reset */
> - user->idx = log_first_idx;
> - user->seq = log_first_seq;
> - ret = -EPIPE;
> - logbuf_unlock_irq();
> - goto out;
> + if (user->entry.seq - user->last_seq != 1) {
> + DECLARE_PRINTKRB_SEQENTRY(e);
> + DECLARE_PRINTKRB_ITER(i, prb, &e);
> + u64 last_seq;
> +
> + prb_iter_peek_next_entry(&i, &last_seq);
> + if (last_seq > user->last_seq) {
> + /* a record was missed, return error and reset */
> + prb_iter_sync(&user->iter, &i);
> + user->last_seq = last_seq;
> + ret = -EPIPE;
> + logbuf_unlock_irq();
> + goto out;
> + }
> }
>
> - msg = log_from_idx(user->idx);
> + user->last_seq = user->entry.seq;
> +
> + msg = (struct printk_log *)&user->entry.buffer[0];
> len = msg_print_ext_header(user->buf, sizeof(user->buf),
> - msg, user->seq);
> + msg, user->last_seq);
> len += msg_print_ext_body(user->buf + len, sizeof(user->buf) - len,
> log_dict(msg), msg->dict_len,
> log_text(msg), msg->text_len);
>
> - user->idx = log_next(user->idx);
> - user->seq++;
> logbuf_unlock_irq();
>
> if (len > count) {
> ret = -EINVAL;
> - goto out;
> + goto restore_out;
> }
>
> if (copy_to_user(buf, user->buf, len)) {
> ret = -EFAULT;
> - goto out;
> + goto restore_out;
> }
> ret = len;
> + goto out;
> +restore_out:
> + prb_iter_copy(&user->iter, &backup_iter);
> + user->last_seq = user->entry.seq - 1;
> out:
> mutex_unlock(&user->lock);
> return ret;
> @@ -935,8 +881,7 @@ static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
> switch (whence) {
> case SEEK_SET:
> /* the first record */
> - user->idx = log_first_idx;
> - user->seq = log_first_seq;
> + user->last_seq = prb_iter_seek(&user->iter, 0);
> break;
> case SEEK_DATA:
> /*
> @@ -944,13 +889,11 @@ 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->idx = clear_idx;
> - user->seq = clear_seq;
> + user->last_seq = prb_iter_seek(&user->iter, clear_last_seq);
> break;
> case SEEK_END:
> /* after the last record */
> - user->idx = log_next_idx;
> - user->seq = log_next_seq;
> + user->last_seq = prb_iter_seek(&user->iter, -1);
> break;
> default:
> ret = -EINVAL;
> @@ -963,19 +906,39 @@ static __poll_t devkmsg_poll(struct file *file, poll_table *wait)
> {
> struct devkmsg_user *user = file->private_data;
> __poll_t ret = 0;
> + u64 last_seq;
>
> if (!user)
> return EPOLLERR|EPOLLNVAL;
>
> - poll_wait(file, &log_wait, wait);
> + poll_wait(file, prb_wait_queue(prb), wait);
>
> logbuf_lock_irq();
> - if (user->seq < log_next_seq) {
> - /* return error when data has vanished underneath us */
> - if (user->seq < log_first_seq)
> - ret = EPOLLIN|EPOLLRDNORM|EPOLLERR|EPOLLPRI;
> - else
> - ret = EPOLLIN|EPOLLRDNORM;
> + if (prb_iter_peek_next_entry(&user->iter, &last_seq)) {
> + ret = EPOLLIN|EPOLLRDNORM;
> + if (last_seq - user->last_seq != 1) {
> + DECLARE_PRINTKRB_SEQENTRY(e);
> + DECLARE_PRINTKRB_ITER(i, prb, &e);
> + u64 last_seq;
> +
> + /*
> + * The sequence number has jumped. This might mean
> + * that the ringbuffer has overtaken the reader,
> + * which would mean that the sequence number previous
> + * the first entry will now be later than the last
> + * entry the reader has seen.
> + *
> + * If instead the sequence number jump is due to
> + * iterating over invalid entries, there is no error.
> + */
> +
> + /* get the sequence number previous the first entry */
> + prb_iter_peek_next_entry(&i, &last_seq);
> +
> + /* return error when data has vanished underneath us */
> + if (last_seq > user->last_seq)
> + ret |= EPOLLERR|EPOLLPRI;
> + }
> }
> logbuf_unlock_irq();
>
> @@ -1008,8 +971,10 @@ static int devkmsg_open(struct inode *inode, struct file *file)
> mutex_init(&user->lock);
>
> logbuf_lock_irq();
> - user->idx = log_first_idx;
> - user->seq = log_first_seq;
> + user->entry.buffer = &user->msgbuf[0];
> + user->entry.buffer_size = sizeof(user->msgbuf);
> + prb_iter_init(&user->iter, prb, &user->entry);
> + prb_iter_peek_next_entry(&user->iter, &user->last_seq);
> logbuf_unlock_irq();
>
> file->private_data = user;
> @@ -1050,11 +1015,8 @@ const struct file_operations kmsg_fops = {
> */
> void log_buf_vmcoreinfo_setup(void)
> {
> - VMCOREINFO_SYMBOL(log_buf);
> - VMCOREINFO_SYMBOL(log_buf_len);
> - VMCOREINFO_SYMBOL(log_first_idx);
> - VMCOREINFO_SYMBOL(clear_idx);
> - VMCOREINFO_SYMBOL(log_next_idx);
> + VMCOREINFO_SYMBOL(printk_rb_static);
> + VMCOREINFO_SYMBOL(printk_rb_dynamic);
> /*
> * Export struct printk_log size and field offsets. User space tools can
> * parse it and detect any changes to structure down the line.
> @@ -1136,13 +1098,36 @@ static void __init log_buf_add_cpu(void)
> static inline void log_buf_add_cpu(void) {}
> #endif /* CONFIG_SMP */
>
> +static void __init add_to_rb(struct printk_ringbuffer *rb,
> + struct prb_entry *e)
> +{
> + struct printk_log *msg = (struct printk_log *)&e->buffer[0];
> + struct prb_reserved_entry re;
> + int size;
> + char *b;
> +
> + size = sizeof(*msg) + msg->text_len + msg->dict_len;
> +
> + b = prb_reserve(&re, rb, size);
> + if (!IS_ERR(b)) {
> + memcpy(b, msg, size);
> + prb_commit(&re);
> + }
> +}
> +
> +static char setup_buf[PRINTK_RECORD_MAX] __initdata;
> +
> void __init setup_log_buf(int early)
> {
> + struct prb_desc *new_descs;
> + struct prb_iterator i;
> unsigned long flags;
> + struct prb_entry e;
> char *new_log_buf;
> unsigned int free;
> + int l;
>
> - if (log_buf != __log_buf)
> + if (prb != &printk_rb_static)
> return;
>
> if (!early && !new_log_buf_len)
> @@ -1151,19 +1136,47 @@ void __init setup_log_buf(int early)
> if (!new_log_buf_len)
> return;
>
> + if (!is_power_of_2(new_log_buf_len))
> + return;
> +
> new_log_buf = memblock_alloc(new_log_buf_len, LOG_ALIGN);
> if (unlikely(!new_log_buf)) {
> - pr_err("log_buf_len: %lu bytes not available\n",
> - new_log_buf_len);
> + pr_err("log_buf_len: %lu data bytes not available\n",
> + new_log_buf_len);
> + return;
> + }
> +
> + new_descs = memblock_alloc((new_log_buf_len >> PRB_AVGBITS) *
> + sizeof(struct prb_desc), LOG_ALIGN);
> + if (unlikely(!new_descs)) {
> + pr_err("log_buf_len: %lu desc bytes not available\n",
> + new_log_buf_len >> PRB_AVGBITS);
> + memblock_free(__pa(new_log_buf), new_log_buf_len);
> return;
> }
>
> + e.buffer = &setup_buf[0];
> + e.buffer_size = sizeof(setup_buf);
> +
> logbuf_lock_irqsave(flags);
> - log_buf_len = new_log_buf_len;
> - log_buf = new_log_buf;
> - new_log_buf_len = 0;
> - free = __LOG_BUF_LEN - log_next_idx;
> - memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
> +
> + prb_init(&printk_rb_dynamic, new_log_buf,
> + bits_per(new_log_buf_len) - 1, new_descs,
> + (bits_per(new_log_buf_len) - 1) - PRB_AVGBITS, &log_wait);
> +
> + free = __LOG_BUF_LEN;
> + prb_for_each_entry(&i, &printk_rb_static, &e, l) {
> + add_to_rb(&printk_rb_dynamic, &e);
> + free -= l;
> + }
> +
> + prb_iter_init(&syslog_iter, &printk_rb_dynamic, &syslog_entry);
> + prb_iter_init(&console_iter, &printk_rb_dynamic, &console_entry);
> +
> + prb_iter_seek(&console_iter, e.seq);
> +
> + prb = &printk_rb_dynamic;
> +
> logbuf_unlock_irqrestore(flags);
>
> pr_info("log_buf_len: %u bytes\n", log_buf_len);
> @@ -1340,29 +1353,43 @@ static size_t msg_print_text(const struct printk_log *msg, bool syslog,
> static int syslog_print(char __user *buf, int size)
> {
> char *text;
> + struct prb_iterator iter;
> struct printk_log *msg;
> + struct prb_entry e;
> + char *msgbuf;
> int len = 0;
>
> text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL);
> if (!text)
> return -ENOMEM;
> + msgbuf = kmalloc(PRINTK_RECORD_MAX, GFP_KERNEL);
> + if (!msgbuf) {
> + kfree(text);
> + return -ENOMEM;
> + }
> +
> + e.buffer = msgbuf;
> + e.buffer_size = PRINTK_RECORD_MAX;
> + prb_iter_init(&iter, prb, &e);
> + msg = (struct printk_log *)msgbuf;
>
> while (size > 0) {
> size_t n;
> size_t skip;
>
> logbuf_lock_irq();
> - if (syslog_seq < log_first_seq) {
> - /* messages are gone, move to first one */
> - syslog_seq = log_first_seq;
> - syslog_idx = log_first_idx;
> - syslog_partial = 0;
> - }
> - if (syslog_seq == log_next_seq) {
> + prb_iter_sync(&iter, &syslog_iter);
> + if (prb_iter_next_valid_entry(&iter) == 0) {
> logbuf_unlock_irq();
> break;
> }
>
> + if (e.seq - syslog_last_seq != 1) {
> + /* messages are gone, move to first one */
> + syslog_last_seq = e.seq - 1;
> + syslog_partial = 0;
> + }
> +
> /*
> * To keep reading/counting partial line consistent,
> * use printk_time value as of the beginning of a line.
> @@ -1371,16 +1398,15 @@ static int syslog_print(char __user *buf, int size)
> syslog_time = printk_time;
>
> skip = syslog_partial;
> - msg = log_from_idx(syslog_idx);
> n = msg_print_text(msg, true, syslog_time, text,
> LOG_LINE_MAX + PREFIX_MAX);
> if (n - syslog_partial <= size) {
> /* message fits into buffer, move forward */
> - syslog_idx = log_next(syslog_idx);
> - syslog_seq++;
> + prb_iter_sync(&syslog_iter, &iter);
> + syslog_last_seq++;
> n -= syslog_partial;
> syslog_partial = 0;
> - } else if (!len){
> + } else if (!len) {
> /* partial read(), remember position */
> n = size;
> syslog_partial += n;
> @@ -1402,22 +1428,73 @@ static int syslog_print(char __user *buf, int size)
> buf += n;
> }
>
> + kfree(msgbuf);
> kfree(text);
> return len;
> }
>
> +/**
> + * count_remaining() - Count the text bytes in following entries.
> + *
> + * @iter: The iterator to use for counting.
> + *
> + * @until_seq: A sequence number to stop counting at.
> + * The entry with this sequence number is not counted.
> + *
> + * Note that although this function will not modify @iter, it does make
> + * use of the prb_entry of @iter.
> + *
> + * Return: The number of bytes of text counted.
> + */
> +static int count_remaining(struct prb_iterator *iter, const u64 until_seq)
> +{
> + bool time = syslog_partial ? syslog_time : printk_time;
> + struct printk_log *msg;
> + struct prb_iterator i;
> + struct prb_entry *e;
> + int len = 0;
> +
> + prb_iter_copy(&i, iter);
> + e = prb_iter_entry(&i);
> + msg = (struct printk_log *)&e->buffer[0];
> +
> + for (;;) {
> + if (prb_iter_next_valid_entry(&i) == 0)
> + break;
> +
> + if (e->seq >= until_seq)
> + break;
> +
> + len += msg_print_text(msg, true, time, NULL, 0);
> + time = printk_time;
> + }
> +
> + return len;
> +}
> +
> static int syslog_print_all(char __user *buf, int size, bool clear)
> {
> + struct prb_iterator iter;
> + struct printk_log *msg;
> + struct prb_entry e;
> + char *msgbuf;
> + int textlen;
> char *text;
> int len = 0;
> - u64 next_seq;
> - u64 seq;
> - u32 idx;
> bool time;
>
> text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL);
> if (!text)
> return -ENOMEM;
> + msgbuf = kmalloc(PRINTK_RECORD_MAX, GFP_KERNEL);
> + if (!msgbuf) {
> + kfree(text);
> + return -ENOMEM;
> + }
> +
> + e.buffer = msgbuf;
> + e.buffer_size = PRINTK_RECORD_MAX;
> + msg = (struct printk_log *)msgbuf;
>
> time = printk_time;
> logbuf_lock_irq();
> @@ -1425,73 +1502,65 @@ 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 = clear_seq;
> - idx = clear_idx;
> - while (seq < log_next_seq) {
> - struct printk_log *msg = log_from_idx(idx);
> -
> - len += msg_print_text(msg, true, time, NULL, 0);
> - idx = log_next(idx);
> - seq++;
> - }
> + prb_iter_init(&iter, prb, &e);
> + prb_iter_seek(&iter, clear_last_seq);
> + len = count_remaining(&iter, -1);
>
> - /* move first record forward until length fits into the buffer */
> - seq = clear_seq;
> - idx = clear_idx;
> - while (len > size && seq < log_next_seq) {
> - struct printk_log *msg = log_from_idx(idx);
> + /* move iterator forward until text fits into the buffer */
> + while (len > size) {
> + if (prb_iter_next_valid_entry(&iter) == 0)
> + break;
>
> len -= msg_print_text(msg, true, time, NULL, 0);
> - idx = log_next(idx);
> - seq++;
> }
>
> - /* last message fitting into this dump */
> - next_seq = log_next_seq;
> -
> + /* copy the rest of the messages into the buffer */
> len = 0;
> - while (len >= 0 && seq < next_seq) {
> - struct printk_log *msg = log_from_idx(idx);
> - int textlen = msg_print_text(msg, true, time, text,
> - LOG_LINE_MAX + PREFIX_MAX);
> + for (;;) {
> + if (prb_iter_next_valid_entry(&iter) == 0)
> + break;
> +
> + textlen = msg_print_text(msg, true, time, text,
> + LOG_LINE_MAX + PREFIX_MAX);
>
> - idx = log_next(idx);
> - seq++;
> + if (len + textlen > size)
> + break;
>
> logbuf_unlock_irq();
> - if (copy_to_user(buf + len, text, textlen))
> + if (copy_to_user(buf + len, text, textlen)) {
> len = -EFAULT;
> - else
> - len += textlen;
> + logbuf_lock_irq();
> + break;
> + }
> logbuf_lock_irq();
>
> - if (seq < log_first_seq) {
> - /* messages are gone, move to next one */
> - seq = log_first_seq;
> - idx = log_first_idx;
> - }
> - }
> + len += textlen;
>
> - if (clear) {
> - clear_seq = log_next_seq;
> - clear_idx = log_next_idx;
> + if (clear)
> + clear_last_seq = e.seq;
> }
> logbuf_unlock_irq();
>
> + kfree(msgbuf);
> kfree(text);
> return len;
> }
>
> static void syslog_clear(void)
> {
> + DECLARE_PRINTKRB_SEQENTRY(e);
> + DECLARE_PRINTKRB_ITER(i, prb, &e);
> +
> logbuf_lock_irq();
> - clear_seq = log_next_seq;
> - clear_idx = log_next_idx;
> + prb_iter_sync(&i, &syslog_iter);
> + clear_last_seq = prb_iter_seek(&i, -1);
> logbuf_unlock_irq();
> }
>
> int do_syslog(int type, char __user *buf, int len, int source)
> {
> + DECLARE_PRINTKRB_SEQENTRY(e);
> + DECLARE_PRINTKRB_ITER(iter, prb, &e);
> bool clear = false;
> static int saved_console_loglevel = LOGLEVEL_DEFAULT;
> int error;
> @@ -1512,10 +1581,15 @@ int do_syslog(int type, char __user *buf, int len, int source)
> return 0;
> if (!access_ok(buf, len))
> return -EFAULT;
> - error = wait_event_interruptible(log_wait,
> - syslog_seq != log_next_seq);
> - if (error)
> +
> + logbuf_lock_irq();
> + prb_iter_sync(&iter, &syslog_iter);
> + logbuf_unlock_irq();
> +
> + error = prb_iter_wait_next_valid_entry(&iter);
> + if (error < 0)
> return error;
> +
> error = syslog_print(buf, len);
> break;
> /* Read/clear last kernel messages */
> @@ -1562,33 +1636,15 @@ int do_syslog(int type, char __user *buf, int len, int source)
> /* Number of chars in the log buffer */
> case SYSLOG_ACTION_SIZE_UNREAD:
> logbuf_lock_irq();
> - if (syslog_seq < log_first_seq) {
> - /* messages are gone, move to first one */
> - syslog_seq = log_first_seq;
> - syslog_idx = log_first_idx;
> - syslog_partial = 0;
> - }
> if (source == SYSLOG_FROM_PROC) {
> /*
> * Short-cut for poll(/"proc/kmsg") which simply checks
> - * for pending data, not the size; return the count of
> - * records, not the length.
> + * for pending data, not the size; return true if there
> + * is a pending record
> */
> - error = log_next_seq - syslog_seq;
> + error = prb_iter_peek_next_entry(&syslog_iter, NULL);
> } else {
> - u64 seq = syslog_seq;
> - u32 idx = syslog_idx;
> - bool time = syslog_partial ? syslog_time : printk_time;
> -
> - while (seq < log_next_seq) {
> - struct printk_log *msg = log_from_idx(idx);
> -
> - error += msg_print_text(msg, true, time, NULL,
> - 0);
> - time = printk_time;
> - idx = log_next(idx);
> - seq++;
> - }
> + error = count_remaining(&syslog_iter, -1);
> error -= syslog_partial;
> }
> logbuf_unlock_irq();
> @@ -1948,7 +2004,6 @@ asmlinkage int vprintk_emit(int facility, int level,
> int printed_len;
> bool in_sched = false, pending_output;
> unsigned long flags;
> - u64 curr_log_seq;
>
> /* Suppress unimportant messages after panic happens */
> if (unlikely(suppress_printk))
> @@ -1964,9 +2019,8 @@ asmlinkage int vprintk_emit(int facility, int level,
>
> /* This stops the holder of console_sem just where we want him */
> logbuf_lock_irqsave(flags);
> - curr_log_seq = log_next_seq;
> printed_len = vprintk_store(facility, level, dict, dictlen, fmt, args);
> - pending_output = (curr_log_seq != log_next_seq);
> + pending_output = prb_iter_peek_next_entry(&console_iter, NULL);
> logbuf_unlock_irqrestore(flags);
>
> /* If called from the scheduler, we can not call up(). */
> @@ -2056,18 +2110,15 @@ EXPORT_SYMBOL(printk);
> #define PREFIX_MAX 0
> #define printk_time false
>
> -static u64 syslog_seq;
> -static u32 syslog_idx;
> -static u64 console_seq;
> -static u32 console_idx;
> +DECLARE_PRINTKRB_SEQENTRY(syslog_entry);
> +DECLARE_PRINTKRB_ITER(syslog_iter, NULL, NULL);
> +DECLARE_PRINTKRB_SEQENTRY(console_entry);
> +DECLARE_PRINTKRB_ITER(console_iter, NULL, NULL);
> +
> +static u64 console_last_seq;
> static u64 exclusive_console_stop_seq;
> -static u64 log_first_seq;
> -static u32 log_first_idx;
> -static u64 log_next_seq;
> static char *log_text(const struct printk_log *msg) { return NULL; }
> static char *log_dict(const struct printk_log *msg) { return NULL; }
> -static struct printk_log *log_from_idx(u32 idx) { return NULL; }
> -static u32 log_next(u32 idx) { return 0; }
> static ssize_t msg_print_ext_header(char *buf, size_t size,
> struct printk_log *msg,
> u64 seq) { return 0; }
> @@ -2402,36 +2453,32 @@ void console_unlock(void)
>
> printk_safe_enter_irqsave(flags);
> raw_spin_lock(&logbuf_lock);
> - if (console_seq < log_first_seq) {
> - len = sprintf(text,
> - "** %llu printk messages dropped **\n",
> - log_first_seq - console_seq);
> +skip:
> + if (prb_iter_next_valid_entry(&console_iter) == 0)
> + break;
>
> - /* messages are gone, move to first one */
> - console_seq = log_first_seq;
> - console_idx = log_first_idx;
> + if (console_entry.seq - console_last_seq != 1) {
> + len = sprintf(text,
> + "** %llu printk messages dropped **\n",
> + console_entry.seq - (console_last_seq + 1));
> } else {
> len = 0;
> }
> -skip:
> - if (console_seq == log_next_seq)
> - break;
> + console_last_seq = console_entry.seq;
>
> - msg = log_from_idx(console_idx);
> + msg = (struct printk_log *)&console_entry.buffer[0];
> if (suppress_message_printing(msg->level)) {
> /*
> * Skip record we have buffered and already printed
> * directly to the console when we received it, and
> * record that has level above the console loglevel.
> */
> - console_idx = log_next(console_idx);
> - console_seq++;
> goto skip;
> }
>
> /* Output to all consoles once old messages replayed. */
> if (unlikely(exclusive_console &&
> - console_seq >= exclusive_console_stop_seq)) {
> + console_last_seq > exclusive_console_stop_seq)) {
> exclusive_console = NULL;
> }
>
> @@ -2441,14 +2488,12 @@ void console_unlock(void)
> if (nr_ext_console_drivers) {
> ext_len = msg_print_ext_header(ext_text,
> sizeof(ext_text),
> - msg, console_seq);
> + msg, console_last_seq);
> ext_len += msg_print_ext_body(ext_text + ext_len,
> sizeof(ext_text) - ext_len,
> log_dict(msg), msg->dict_len,
> log_text(msg), msg->text_len);
> }
> - console_idx = log_next(console_idx);
> - console_seq++;
> raw_spin_unlock(&logbuf_lock);
>
> /*
> @@ -2487,7 +2532,7 @@ void console_unlock(void)
> * flush, no worries.
> */
> raw_spin_lock(&logbuf_lock);
> - retry = console_seq != log_next_seq;
> + retry = prb_iter_peek_next_entry(&console_iter, NULL);
> raw_spin_unlock(&logbuf_lock);
> printk_safe_exit_irqrestore(flags);
>
> @@ -2556,8 +2601,8 @@ void console_flush_on_panic(enum con_flush_mode mode)
> unsigned long flags;
>
> logbuf_lock_irqsave(flags);
> - console_seq = log_first_seq;
> - console_idx = log_first_idx;
> + console_last_seq = 0;
> + prb_iter_seek(&console_iter, 0);
> logbuf_unlock_irqrestore(flags);
> }
> console_unlock();
> @@ -2760,8 +2805,7 @@ void register_console(struct console *newcon)
> * for us.
> */
> logbuf_lock_irqsave(flags);
> - console_seq = syslog_seq;
> - console_idx = syslog_idx;
> + prb_iter_sync(&console_iter, &syslog_iter);
> /*
> * We're about to replay the log buffer. Only do this to the
> * just-registered console to avoid excessive message spam to
> @@ -2772,7 +2816,8 @@ void register_console(struct console *newcon)
> * ignores console_lock.
> */
> exclusive_console = newcon;
> - exclusive_console_stop_seq = console_seq;
> + exclusive_console_stop_seq = console_last_seq;
> + console_last_seq = 0;
> logbuf_unlock_irqrestore(flags);
> }
> console_unlock();
> @@ -3033,6 +3078,8 @@ EXPORT_SYMBOL(printk_timed_ratelimit);
> static DEFINE_SPINLOCK(dump_list_lock);
> static LIST_HEAD(dump_list);
>
> +static char kmsg_dump_msgbuf[PRINTK_RECORD_MAX];
> +
> /**
> * kmsg_dump_register - register a kernel log dumper.
> * @dumper: pointer to the kmsg_dumper structure
> @@ -3102,7 +3149,6 @@ module_param_named(always_kmsg_dump, always_kmsg_dump, bool, S_IRUGO | S_IWUSR);
> void kmsg_dump(enum kmsg_dump_reason reason)
> {
> struct kmsg_dumper *dumper;
> - unsigned long flags;
>
> if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
> return;
> @@ -3115,12 +3161,7 @@ void kmsg_dump(enum kmsg_dump_reason reason)
> /* initialize iterator with data about the stored records */
> dumper->active = true;
>
> - logbuf_lock_irqsave(flags);
> - dumper->cur_seq = clear_seq;
> - dumper->cur_idx = clear_idx;
> - dumper->next_seq = log_next_seq;
> - dumper->next_idx = log_next_idx;
> - logbuf_unlock_irqrestore(flags);
> + kmsg_dump_rewind(dumper);
>
> /* invoke dumper which will iterate over records */
> dumper->dump(dumper, reason);
> @@ -3153,28 +3194,27 @@ void kmsg_dump(enum kmsg_dump_reason reason)
> bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper, bool syslog,
> char *line, size_t size, size_t *len)
> {
> - struct printk_log *msg;
> + struct prb_entry e = {
> + .buffer = &kmsg_dump_msgbuf[0],
> + .buffer_size = sizeof(kmsg_dump_msgbuf),
> + };
> + DECLARE_PRINTKRB_ITER(i, prb, &e);
> + struct printk_log *msg = (struct printk_log *)&e.buffer[0];
> size_t l = 0;
> bool ret = false;
>
> if (!dumper->active)
> goto out;
>
> - if (dumper->cur_seq < log_first_seq) {
> - /* messages are gone, move to first available one */
> - dumper->cur_seq = log_first_seq;
> - dumper->cur_idx = log_first_idx;
> - }
> + dumper->last_seq = prb_iter_seek(&i, dumper->last_seq);
>
> /* last entry */
> - if (dumper->cur_seq >= log_next_seq)
> + if (prb_iter_next_valid_entry(&i) == 0)
> goto out;
>
> - msg = log_from_idx(dumper->cur_idx);
> l = msg_print_text(msg, syslog, printk_time, line, size);
>
> - dumper->cur_idx = log_next(dumper->cur_idx);
> - dumper->cur_seq++;
> + dumper->last_seq = e.seq;
> ret = true;
> out:
> if (len)
> @@ -3235,11 +3275,14 @@ EXPORT_SYMBOL_GPL(kmsg_dump_get_line);
> bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
> char *buf, size_t size, size_t *len)
> {
> + struct prb_entry e = {
> + .buffer = &kmsg_dump_msgbuf[0],
> + .buffer_size = sizeof(kmsg_dump_msgbuf),
> + };
> + DECLARE_PRINTKRB_ITER(i, prb, &e);
> + struct printk_log *msg = (struct printk_log *)&e.buffer[0];
> unsigned long flags;
> - u64 seq;
> - u32 idx;
> - u64 next_seq;
> - u32 next_idx;
> + u64 next_until_seq;
> size_t l = 0;
> bool ret = false;
> bool time = printk_time;
> @@ -3248,55 +3291,45 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
> goto out;
>
> logbuf_lock_irqsave(flags);
> - if (dumper->cur_seq < log_first_seq) {
> - /* messages are gone, move to first available one */
> - dumper->cur_seq = log_first_seq;
> - dumper->cur_idx = log_first_idx;
> - }
> +
> + if (!dumper->until_seq)
> + dumper->until_seq = -1;
> +
> + dumper->last_seq = prb_iter_seek(&i, dumper->last_seq);
>
> /* last entry */
> - if (dumper->cur_seq >= dumper->next_seq) {
> + if (!prb_iter_peek_next_entry(&i, NULL)) {
> logbuf_unlock_irqrestore(flags);
> goto out;
> }
>
> /* calculate length of entire buffer */
> - seq = dumper->cur_seq;
> - idx = dumper->cur_idx;
> - while (seq < dumper->next_seq) {
> - struct printk_log *msg = log_from_idx(idx);
> + l = count_remaining(&i, dumper->until_seq);
>
> - l += msg_print_text(msg, true, time, NULL, 0);
> - idx = log_next(idx);
> - seq++;
> - }
> -
> - /* move first record forward until length fits into the buffer */
> - seq = dumper->cur_seq;
> - idx = dumper->cur_idx;
> - while (l > size && seq < dumper->next_seq) {
> - struct printk_log *msg = log_from_idx(idx);
> + if (l <= size) {
> + /* last message in next iteration */
> + next_until_seq = dumper->last_seq;
> + } else {
> + /* move iterator forward until text fits into the buffer */
> + while (l > size) {
> + prb_iter_next_valid_entry(&i);
> + l -= msg_print_text(msg, true, time, NULL, 0);
> + }
>
> - l -= msg_print_text(msg, true, time, NULL, 0);
> - idx = log_next(idx);
> - seq++;
> + /* last message in next iteration */
> + next_until_seq = e.seq + 1;
> }
>
> - /* last message in next interation */
> - next_seq = seq;
> - next_idx = idx;
> -
> + /* copy messages to buffer */
> l = 0;
> - while (seq < dumper->next_seq) {
> - struct printk_log *msg = log_from_idx(idx);
> -
> + for (;;) {
> + prb_iter_next_valid_entry(&i);
> + if (e.seq >= dumper->until_seq)
> + break;
> l += msg_print_text(msg, syslog, time, buf + l, size - l);
> - idx = log_next(idx);
> - seq++;
> }
>
> - dumper->next_seq = next_seq;
> - dumper->next_idx = next_idx;
> + dumper->until_seq = next_until_seq;
> ret = true;
> logbuf_unlock_irqrestore(flags);
> out:
> @@ -3318,10 +3351,8 @@ EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
> */
> void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
> {
> - dumper->cur_seq = clear_seq;
> - dumper->cur_idx = clear_idx;
> - dumper->next_seq = log_next_seq;
> - dumper->next_idx = log_next_idx;
> + dumper->last_seq = clear_last_seq;
> + dumper->until_seq = 0;
> }
>
> /**
> diff --git a/kernel/printk/ringbuffer.h b/kernel/printk/ringbuffer.h
> index 70cb9ad284d4..02b4c53e287e 100644
> --- a/kernel/printk/ringbuffer.h
> +++ b/kernel/printk/ringbuffer.h
> @@ -134,6 +134,8 @@ struct prb_iterator {
> unsigned long next_id;
> };
>
> +#ifdef CONFIG_PRINTK
> +
> /* writer interface */
> char *prb_reserve(struct prb_reserved_entry *e, struct printk_ringbuffer *rb,
> unsigned long size);
> @@ -163,6 +165,28 @@ struct nl_node *prb_desc_node(unsigned long id, void *arg);
> bool prb_desc_busy(unsigned long id, void *arg);
> struct dr_desc *prb_getdesc(unsigned long id, void *arg);
>
> +#else /* CONFIG_PRINTK */
> +
> +#define prb_reserve(e, rb, size) NULL
> +#define prb_commit(e)
> +#define prb_iter_init(iter, rb, e)
> +#define prb_iter_next_valid_entry(iter) 0
> +#define prb_iter_wait_next_valid_entry(iter) -ERESTARTSYS
> +#define prb_iter_sync(dst, src)
> +#define prb_iter_copy(dst, src)
> +#define prb_iter_peek_next_entry(iter, last_seq) false
> +#define prb_iter_seek(iter, last_seq) 0
> +#define prb_wait_queue(rb) NULL
> +#define prb_iter_entry(iter) NULL
> +#define prb_getfail(rb) 0
> +#define prb_init(rb, data, data_size_bits, descs, desc_count_bits, waitq)
> +#define prb_unused(rb) 0
> +#define prb_desc_node NULL
> +#define prb_desc_busy NULL
> +#define prb_getdesc NULL
> +
> +#endif /* CONFIG_PRINTK */
> +
> /**
> * DECLARE_PRINTKRB() - Declare a printk ringbuffer.
> *
> --
> 2.20.1
>

Thanks
Dave