[RFC PATCH v1 22/25] printk: implement /dev/kmsg

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxx>
---
 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




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux