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.ogn...@linutronix.de>
---
 fs/proc/kmsg.c         |   4 +-
 include/linux/printk.h |   1 +
 kernel/printk/printk.c | 162 +++++++++++++++++++++++++++++++++----------------
 3 files changed, 113 insertions(+), 54 deletions(-)

diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index 4f4a2abb225e..4e62963a87ca 100644
--- 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 *file, char __user *buf,
 
 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;
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 58bd06d88ea3..bef0b5c5fcbf 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -191,6 +191,7 @@ __printf(1, 2) void dump_stack_set_arch_desc(const char 
*fmt, ...);
 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)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 306e7575499c..ed1ec8c23e97 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -637,10 +637,11 @@ static ssize_t msg_print_ext_body(char *buf, size_t size,
 /* /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
@@ -723,9 +724,11 @@ 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;
+       size_t len;
+       u64 seq;
 
        if (!user)
                return -EBADF;
@@ -734,52 +737,67 @@ static ssize_t devkmsg_read(struct file *file, char 
__user *buf,
        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;
@@ -788,19 +806,21 @@ static ssize_t devkmsg_read(struct file *file, char 
__user *buf,
 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:
                /*
@@ -808,40 +828,83 @@ 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;
+               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;
 }
@@ -871,10 +934,7 @@ 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;
-       logbuf_unlock_irq();
+       prb_iter_init(&user->iter, &printk_rb, &user->seq);
 
        file->private_data = user;
        return 0;
-- 
2.11.0

Reply via email to