[RFC v5 3/8] kmsg: introduce additional kmsg devices support

2015-10-27 Thread Paul Osmialowski
From: Marcin Niesluchowski 

kmsg device provides operations on cyclic logging buffer used mainly
by kernel but also in userspace by privileged processes.

Additional kmsg devices keep the same log format but may be added
dynamically with custom size.

Signed-off-by: Marcin Niesluchowski 
Signed-off-by: Paul Osmialowski 
---
 fs/proc/kmsg.c |   4 +-
 kernel/printk/kmsg.c   | 301 --
 kernel/printk/printk.c | 317 ++---
 kernel/printk/printk.h |  69 +++
 4 files changed, 431 insertions(+), 260 deletions(-)

diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index 05f8dcd..0d354e4 100644
--- a/fs/proc/kmsg.c
+++ b/fs/proc/kmsg.c
@@ -17,7 +17,7 @@
 #include 
 #include 
 
-extern wait_queue_head_t log_wait;
+extern wait_queue_head_t *log_wait;
 
 static int kmsg_open(struct inode * inode, struct file * file)
 {
@@ -41,7 +41,7 @@ static ssize_t kmsg_read(struct file *file, char __user *buf,
 
 static unsigned int kmsg_poll(struct file *file, poll_table *wait)
 {
-   poll_wait(file, _wait, wait);
+   poll_wait(file, log_wait, wait);
if (do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_PROC))
return POLLIN | POLLRDNORM;
return 0;
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 02981a7..42e784bd 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -30,6 +30,34 @@ struct devkmsg_user {
char buf[CONSOLE_EXT_LOG_MAX];
 };
 
+static int kmsg_sys_write(int minor, int level, const char *fmt, ...)
+{
+   va_list args;
+   int ret = -ENXIO;
+   struct log_buffer *log_b;
+
+   rcu_read_lock();
+   list_for_each_entry_rcu(log_b, _buf.list, list) {
+   if (log_b->minor != minor)
+   continue;
+
+   raw_spin_lock(_b->lock);
+
+   va_start(args, fmt);
+   log_format_and_store(log_b, 1 /* LOG_USER */, level,
+NULL, 0, fmt, args);
+   va_end(args);
+   wake_up_interruptible(_b->wait);
+
+   raw_spin_unlock(_b->lock);
+
+   ret = 0;
+   break;
+   }
+   rcu_read_unlock();
+   return ret;
+}
+
 static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from)
 {
char *buf, *line;
@@ -38,6 +66,7 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct 
iov_iter *from)
int facility = 1;   /* LOG_USER */
size_t len = iov_iter_count(from);
ssize_t ret = len;
+   int minor = iminor(iocb->ki_filp->f_inode);
 
if (len > LOG_LINE_MAX)
return -EINVAL;
@@ -75,51 +104,57 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct 
iov_iter *from)
}
}
 
-   printk_emit(facility, level, NULL, 0, "%s", line);
+   if (minor == log_buf.minor) {
+   printk_emit(facility, level, NULL, 0, "%s", line);
+   } else {
+   int error = kmsg_sys_write(minor, level, "%s", line);
+
+   if (error)
+   ret = error;
+   }
+
kfree(buf);
return ret;
 }
 
-static ssize_t devkmsg_read(struct file *file, char __user *buf,
-   size_t count, loff_t *ppos)
+static ssize_t kmsg_read(struct log_buffer *log_b, struct file *file,
+char __user *buf, size_t count, loff_t *ppos)
 {
struct devkmsg_user *user = file->private_data;
struct printk_log *msg;
size_t len;
ssize_t ret;
 
-   if (!user)
-   return -EBADF;
-
ret = mutex_lock_interruptible(>lock);
if (ret)
return ret;
-   raw_spin_lock_irq(_lock);
-   while (user->seq == log_next_seq) {
+
+   raw_spin_lock_irq(_b->lock);
+   while (user->seq == log_b->next_seq) {
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
-   raw_spin_unlock_irq(_lock);
+   raw_spin_unlock_irq(_b->lock);
goto out;
}
 
-   raw_spin_unlock_irq(_lock);
-   ret = wait_event_interruptible(log_wait,
-  user->seq != log_next_seq);
+   raw_spin_unlock_irq(_b->lock);
+   ret = wait_event_interruptible(log_b->wait,
+  user->seq != log_b->next_seq);
if (ret)
goto out;
-   raw_spin_lock_irq(_lock);
+   raw_spin_lock_irq(_b->lock);
}
 
-   if (user->seq < log_first_seq) {
+   if (user->seq < log_b->first_seq) {
/* our last seen message is gone, return error and reset */
-   user->idx = log_first_idx;
-   user->seq = log_first_seq;
+   user->idx = log_b->first_idx;
+   user->seq = 

[RFC v5 3/8] kmsg: introduce additional kmsg devices support

2015-10-27 Thread Paul Osmialowski
From: Marcin Niesluchowski 

kmsg device provides operations on cyclic logging buffer used mainly
by kernel but also in userspace by privileged processes.

Additional kmsg devices keep the same log format but may be added
dynamically with custom size.

Signed-off-by: Marcin Niesluchowski 
Signed-off-by: Paul Osmialowski 
---
 fs/proc/kmsg.c |   4 +-
 kernel/printk/kmsg.c   | 301 --
 kernel/printk/printk.c | 317 ++---
 kernel/printk/printk.h |  69 +++
 4 files changed, 431 insertions(+), 260 deletions(-)

diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index 05f8dcd..0d354e4 100644
--- a/fs/proc/kmsg.c
+++ b/fs/proc/kmsg.c
@@ -17,7 +17,7 @@
 #include 
 #include 
 
-extern wait_queue_head_t log_wait;
+extern wait_queue_head_t *log_wait;
 
 static int kmsg_open(struct inode * inode, struct file * file)
 {
@@ -41,7 +41,7 @@ static ssize_t kmsg_read(struct file *file, char __user *buf,
 
 static unsigned int kmsg_poll(struct file *file, poll_table *wait)
 {
-   poll_wait(file, _wait, wait);
+   poll_wait(file, log_wait, wait);
if (do_syslog(SYSLOG_ACTION_SIZE_UNREAD, NULL, 0, SYSLOG_FROM_PROC))
return POLLIN | POLLRDNORM;
return 0;
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 02981a7..42e784bd 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -30,6 +30,34 @@ struct devkmsg_user {
char buf[CONSOLE_EXT_LOG_MAX];
 };
 
+static int kmsg_sys_write(int minor, int level, const char *fmt, ...)
+{
+   va_list args;
+   int ret = -ENXIO;
+   struct log_buffer *log_b;
+
+   rcu_read_lock();
+   list_for_each_entry_rcu(log_b, _buf.list, list) {
+   if (log_b->minor != minor)
+   continue;
+
+   raw_spin_lock(_b->lock);
+
+   va_start(args, fmt);
+   log_format_and_store(log_b, 1 /* LOG_USER */, level,
+NULL, 0, fmt, args);
+   va_end(args);
+   wake_up_interruptible(_b->wait);
+
+   raw_spin_unlock(_b->lock);
+
+   ret = 0;
+   break;
+   }
+   rcu_read_unlock();
+   return ret;
+}
+
 static ssize_t devkmsg_write(struct kiocb *iocb, struct iov_iter *from)
 {
char *buf, *line;
@@ -38,6 +66,7 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct 
iov_iter *from)
int facility = 1;   /* LOG_USER */
size_t len = iov_iter_count(from);
ssize_t ret = len;
+   int minor = iminor(iocb->ki_filp->f_inode);
 
if (len > LOG_LINE_MAX)
return -EINVAL;
@@ -75,51 +104,57 @@ static ssize_t devkmsg_write(struct kiocb *iocb, struct 
iov_iter *from)
}
}
 
-   printk_emit(facility, level, NULL, 0, "%s", line);
+   if (minor == log_buf.minor) {
+   printk_emit(facility, level, NULL, 0, "%s", line);
+   } else {
+   int error = kmsg_sys_write(minor, level, "%s", line);
+
+   if (error)
+   ret = error;
+   }
+
kfree(buf);
return ret;
 }
 
-static ssize_t devkmsg_read(struct file *file, char __user *buf,
-   size_t count, loff_t *ppos)
+static ssize_t kmsg_read(struct log_buffer *log_b, struct file *file,
+char __user *buf, size_t count, loff_t *ppos)
 {
struct devkmsg_user *user = file->private_data;
struct printk_log *msg;
size_t len;
ssize_t ret;
 
-   if (!user)
-   return -EBADF;
-
ret = mutex_lock_interruptible(>lock);
if (ret)
return ret;
-   raw_spin_lock_irq(_lock);
-   while (user->seq == log_next_seq) {
+
+   raw_spin_lock_irq(_b->lock);
+   while (user->seq == log_b->next_seq) {
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
-   raw_spin_unlock_irq(_lock);
+   raw_spin_unlock_irq(_b->lock);
goto out;
}
 
-   raw_spin_unlock_irq(_lock);
-   ret = wait_event_interruptible(log_wait,
-  user->seq != log_next_seq);
+   raw_spin_unlock_irq(_b->lock);
+   ret = wait_event_interruptible(log_b->wait,
+  user->seq != log_b->next_seq);
if (ret)
goto out;
-   raw_spin_lock_irq(_lock);
+   raw_spin_lock_irq(_b->lock);
}
 
-   if (user->seq < log_first_seq) {
+   if (user->seq < log_b->first_seq) {
/* our last seen message is gone, return error and reset */
-   user->idx = log_first_idx;
-   user->seq =