mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-11-03 05:26:14 +00:00
305 lines
7.6 KiB
Diff
305 lines
7.6 KiB
Diff
From: John Ogness <john.ogness@linutronix.de>
|
|
Date: Tue, 12 Feb 2019 15:30:00 +0100
|
|
Subject: [PATCH 22/25] printk: implement /dev/kmsg
|
|
|
|
Since printk messages are now logged to a new ring buffer, update
|
|
the /dev/kmsg functions to pull the messages from there.
|
|
|
|
Signed-off-by: John Ogness <john.ogness@linutronix.de>
|
|
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
|
---
|
|
fs/proc/kmsg.c | 4 -
|
|
include/linux/printk.h | 1
|
|
kernel/printk/printk.c | 162 +++++++++++++++++++++++++++++++++----------------
|
|
3 files changed, 113 insertions(+), 54 deletions(-)
|
|
|
|
--- a/fs/proc/kmsg.c
|
|
+++ b/fs/proc/kmsg.c
|
|
@@ -18,8 +18,6 @@
|
|
#include <linux/uaccess.h>
|
|
#include <asm/io.h>
|
|
|
|
-extern wait_queue_head_t log_wait;
|
|
-
|
|
static int kmsg_open(struct inode * inode, struct file * file)
|
|
{
|
|
return do_syslog(SYSLOG_ACTION_OPEN, NULL, 0, SYSLOG_FROM_PROC);
|
|
@@ -42,7 +40,7 @@ static ssize_t kmsg_read(struct file *fi
|
|
|
|
static __poll_t kmsg_poll(struct file *file, poll_table *wait)
|
|
{
|
|
- poll_wait(file, &log_wait, wait);
|
|
+ poll_wait(file, printk_wait_queue(), wait);
|
|
if (do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_PROC))
|
|
return EPOLLIN | EPOLLRDNORM;
|
|
return 0;
|
|
--- a/include/linux/printk.h
|
|
+++ b/include/linux/printk.h
|
|
@@ -192,6 +192,7 @@ void __init setup_log_buf(int early);
|
|
void dump_stack_print_info(const char *log_lvl);
|
|
void show_regs_print_info(const char *log_lvl);
|
|
extern asmlinkage void dump_stack(void) __cold;
|
|
+struct wait_queue_head *printk_wait_queue(void);
|
|
#else
|
|
static inline __printf(1, 0)
|
|
int vprintk(const char *s, va_list args)
|
|
--- a/kernel/printk/printk.c
|
|
+++ b/kernel/printk/printk.c
|
|
@@ -673,10 +673,11 @@ static ssize_t msg_print_ext_body(char *
|
|
/* /dev/kmsg - userspace message inject/listen interface */
|
|
struct devkmsg_user {
|
|
u64 seq;
|
|
- u32 idx;
|
|
+ struct prb_iterator iter;
|
|
struct ratelimit_state rs;
|
|
struct mutex lock;
|
|
char buf[CONSOLE_EXT_LOG_MAX];
|
|
+ char msgbuf[PRINTK_RECORD_MAX];
|
|
};
|
|
|
|
static __printf(3, 4) __cold
|
|
@@ -759,9 +760,11 @@ static ssize_t devkmsg_read(struct file
|
|
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;
|
|
+ size_t len;
|
|
+ u64 seq;
|
|
|
|
if (!user)
|
|
return -EBADF;
|
|
@@ -770,52 +773,67 @@ static ssize_t devkmsg_read(struct file
|
|
if (ret)
|
|
return ret;
|
|
|
|
- logbuf_lock_irq();
|
|
- while (user->seq == log_next_seq) {
|
|
- if (file->f_flags & O_NONBLOCK) {
|
|
- ret = -EAGAIN;
|
|
- logbuf_unlock_irq();
|
|
- goto out;
|
|
- }
|
|
+ /* make a backup copy in case there is a problem */
|
|
+ prb_iter_copy(&backup_iter, &user->iter);
|
|
|
|
- logbuf_unlock_irq();
|
|
- ret = wait_event_interruptible(log_wait,
|
|
- user->seq != log_next_seq);
|
|
- if (ret)
|
|
- goto out;
|
|
- logbuf_lock_irq();
|
|
+ if (file->f_flags & O_NONBLOCK) {
|
|
+ ret = prb_iter_next(&user->iter, &user->msgbuf[0],
|
|
+ sizeof(user->msgbuf), &seq);
|
|
+ } else {
|
|
+ ret = prb_iter_wait_next(&user->iter, &user->msgbuf[0],
|
|
+ sizeof(user->msgbuf), &seq);
|
|
}
|
|
-
|
|
- 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;
|
|
+ if (ret == 0) {
|
|
+ /* end of list */
|
|
+ ret = -EAGAIN;
|
|
+ goto out;
|
|
+ } else if (ret == -EINVAL) {
|
|
+ /* iterator invalid, return error and reset */
|
|
ret = -EPIPE;
|
|
- logbuf_unlock_irq();
|
|
+ prb_iter_init(&user->iter, &printk_rb, &user->seq);
|
|
+ goto out;
|
|
+ } else if (ret < 0) {
|
|
+ /* interrupted by signal */
|
|
goto out;
|
|
}
|
|
|
|
- msg = log_from_idx(user->idx);
|
|
+ if (user->seq == 0) {
|
|
+ user->seq = seq;
|
|
+ } else {
|
|
+ user->seq++;
|
|
+ if (user->seq < seq) {
|
|
+ ret = -EPIPE;
|
|
+ goto restore_out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ msg = (struct printk_log *)&user->msgbuf[0];
|
|
len = msg_print_ext_header(user->buf, sizeof(user->buf),
|
|
msg, user->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:
|
|
+ /*
|
|
+ * There was an error, but this message should not be
|
|
+ * lost because of it. Restore the backup and setup
|
|
+ * seq so that it will work with the next read.
|
|
+ */
|
|
+ prb_iter_copy(&user->iter, &backup_iter);
|
|
+ user->seq = seq - 1;
|
|
out:
|
|
mutex_unlock(&user->lock);
|
|
return ret;
|
|
@@ -824,19 +842,21 @@ static ssize_t devkmsg_read(struct file
|
|
static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
|
|
{
|
|
struct devkmsg_user *user = file->private_data;
|
|
- loff_t ret = 0;
|
|
+ loff_t ret;
|
|
|
|
if (!user)
|
|
return -EBADF;
|
|
if (offset)
|
|
return -ESPIPE;
|
|
|
|
- logbuf_lock_irq();
|
|
+ ret = mutex_lock_interruptible(&user->lock);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
switch (whence) {
|
|
case SEEK_SET:
|
|
/* the first record */
|
|
- user->idx = log_first_idx;
|
|
- user->seq = log_first_seq;
|
|
+ prb_iter_init(&user->iter, &printk_rb, &user->seq);
|
|
break;
|
|
case SEEK_DATA:
|
|
/*
|
|
@@ -844,40 +864,83 @@ static loff_t devkmsg_llseek(struct file
|
|
* 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;
|
|
+ for (;;) {
|
|
+ prb_iter_init(&user->iter, &printk_rb, NULL);
|
|
+ ret = prb_iter_seek(&user->iter, clear_seq);
|
|
+ if (ret > 0) {
|
|
+ /* seeked to clear seq */
|
|
+ user->seq = clear_seq;
|
|
+ break;
|
|
+ } else if (ret == 0) {
|
|
+ /*
|
|
+ * The end of the list was hit without
|
|
+ * ever seeing the clear seq. Just
|
|
+ * seek to the beginning of the list.
|
|
+ */
|
|
+ prb_iter_init(&user->iter, &printk_rb,
|
|
+ &user->seq);
|
|
+ break;
|
|
+ }
|
|
+ /* iterator invalid, start over */
|
|
+ }
|
|
+ ret = 0;
|
|
break;
|
|
case SEEK_END:
|
|
/* after the last record */
|
|
- user->idx = log_next_idx;
|
|
- user->seq = log_next_seq;
|
|
+ for (;;) {
|
|
+ ret = prb_iter_next(&user->iter, NULL, 0, &user->seq);
|
|
+ if (ret == 0)
|
|
+ break;
|
|
+ else if (ret > 0)
|
|
+ continue;
|
|
+ /* iterator invalid, start over */
|
|
+ prb_iter_init(&user->iter, &printk_rb, &user->seq);
|
|
+ }
|
|
+ ret = 0;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
- logbuf_unlock_irq();
|
|
+
|
|
+ mutex_unlock(&user->lock);
|
|
return ret;
|
|
}
|
|
|
|
+struct wait_queue_head *printk_wait_queue(void)
|
|
+{
|
|
+ /* FIXME: using prb internals! */
|
|
+ return printk_rb.wq;
|
|
+}
|
|
+
|
|
static __poll_t devkmsg_poll(struct file *file, poll_table *wait)
|
|
{
|
|
struct devkmsg_user *user = file->private_data;
|
|
+ struct prb_iterator iter;
|
|
__poll_t ret = 0;
|
|
+ int rbret;
|
|
+ u64 seq;
|
|
|
|
if (!user)
|
|
return EPOLLERR|EPOLLNVAL;
|
|
|
|
- poll_wait(file, &log_wait, wait);
|
|
+ poll_wait(file, printk_wait_queue(), 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;
|
|
- }
|
|
- logbuf_unlock_irq();
|
|
+ mutex_lock(&user->lock);
|
|
+
|
|
+ /* use copy so no actual iteration takes place */
|
|
+ prb_iter_copy(&iter, &user->iter);
|
|
+
|
|
+ rbret = prb_iter_next(&iter, &user->msgbuf[0],
|
|
+ sizeof(user->msgbuf), &seq);
|
|
+ if (rbret == 0)
|
|
+ goto out;
|
|
+
|
|
+ ret = EPOLLIN|EPOLLRDNORM;
|
|
+
|
|
+ if (rbret < 0 || (seq - user->seq) != 1)
|
|
+ ret |= EPOLLERR|EPOLLPRI;
|
|
+out:
|
|
+ mutex_unlock(&user->lock);
|
|
|
|
return ret;
|
|
}
|
|
@@ -907,10 +970,7 @@ static int devkmsg_open(struct inode *in
|
|
|
|
mutex_init(&user->lock);
|
|
|
|
- logbuf_lock_irq();
|
|
- user->idx = log_first_idx;
|
|
- user->seq = log_first_seq;
|
|
- logbuf_unlock_irq();
|
|
+ prb_iter_init(&user->iter, &printk_rb, &user->seq);
|
|
|
|
file->private_data = user;
|
|
return 0;
|