Reasons and detail of new implementation
The Linux kernel’s fundamental printk() function new implementation is coming with Linux version 5.10.
It is a fully lock-less ring-buffer implementation for printk. This new implementation allows for storing and reading messages without the possibility of deadlocks or relying on temporary per-CPU buffers.
Here is the link describe the particular issues of the old printk and list of changes from the new implementation.
See the detail for decoding printk buffer snapshot
Printk buffer
The buffer of printk is defined as static array (__log_buf) in the printk.c.
This part is no change from the new implementation.
/* record buffer */
#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;
The new implementation defines new organization for the buffer:
"The printk log buffer consists of a sequenced collection of records, each
containing variable length message text. Every record also contains its
own meta-data (@info).
Every record meta-data carries the timestamp in microseconds, as well as
the standard userspace syslog level and syslog facility. The usual kernel
messages use LOG_KERN; userspace-injected messages always carry a matching
syslog facility, by default LOG_USER. The origin of every message can be
reliably determined that way."
From printk_ringbuffer.h:
printk_ringbuffer structure is defined like this. We can see there are 2 ring buffers which one is for description and another one for data buffer.
struct printk_ringbuffer {
struct prb_desc_ring desc_ring;
struct prb_data_ring text_data_ring;
atomic_long_t fail;
};
Ring buffer for data structure is easy to understand:
/* A ringbuffer of "ID + data" elements. */
struct prb_data_ring {
unsigned int size_bits;
char *data;
atomic_long_t head_lpos;
atomic_long_t tail_lpos;
};
Descriptior ring buffer structure is defined as below. It is meta-data for the data buffer.
struct prb_desc_ring {
unsigned int count_bits;
struct prb_desc *descs;
struct printk_info *infos;
atomic_long_t head_id;
atomic_long_t tail_id;
atomic_long_t last_finalized_id;
};
For adding or reading one printk record. A structure is defined also to be used:
struct printk_record {
struct printk_info *info;
char *text_buf;
unsigned int text_buf_size;
};
So if we get a buffer snapshot from Linux. The use case is for current Linux virtual machine. We can make a printk snapshot to check the LVM printk logs in case the system can’t bootup at early stage.
Based on the understanding of the new implementation, below function can be used for decoding the buffer snapshot:
decode_printk_snapshot()
{
//reference : static int devkmsg_open(struct inode *inode, struct file *file)
prb_rec_init_rd(&user->record, &user->info,&user->text_buf[0], sizeof(user->text_buf));
atomic64_set(&user->seq, prb_first_valid_seq(prb)); //prb is the printk buffer
for(;;)
{
//reference: static ssize_t devkmsg_read()
if (prb_read_valid(prb,atomic64_read(&user->seq), r) == false)
break;
len = info_print_ext_header(user->buf, sizeof(user->buf), r->info);
len += msg_print_ext_body(user->buf + len, sizeof(user->buf) - len,
&r->text_buf[0], r->info->text_len,
&r->info->dev_info);
//user->buf is the buffer which store one printk record
atomic64_set(&user->seq, r->info->seq + 1);
}
}