[RFC v4 5/9] kmsg: add function for adding and deleting additional buffers

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

Additional kmsg buffers should be created and deleted dynamically.

Adding two functions
* kmsg_sys_buffer_add() creates additional kmsg buffer returning minor
* kmsg_sys_buffer_del() deletes one based on provided minor

Signed-off-by: Marcin Niesluchowski 
Signed-off-by: Paul Osmialowski 
---
 include/linux/printk.h |   9 +
 kernel/printk/kmsg.c   | 107 +++--
 kernel/printk/printk.c |  12 ++
 kernel/printk/printk.h |   4 ++
 4 files changed, 129 insertions(+), 3 deletions(-)

diff --git a/include/linux/printk.h b/include/linux/printk.h
index 0c4f9de..513fa6f 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -431,6 +431,8 @@ extern const struct file_operations kmsg_fops;
 extern struct device *init_kmsg(int minor, umode_t mode);
 extern int kmsg_memory_open(struct inode *inode, struct file *filp);
 extern int kmsg_mode(int minor, umode_t *mode);
+extern int kmsg_sys_buffer_add(size_t size, umode_t mode);
+extern void kmsg_sys_buffer_del(int minor);
 
 #else
 
@@ -449,6 +451,13 @@ static inline int kmsg_mode(int minor, umode_t *mode)
return -ENXIO;
 }
 
+static inline int kmsg_sys_buffer_add(size_t size, umode_t mode)
+{
+   return -ENXIO;
+}
+
+static inline void kmsg_sys_buffer_del(int minor) {}
+
 #endif
 
 enum {
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 726250f..9222fdc 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -19,6 +19,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 
@@ -141,8 +142,20 @@ static ssize_t kmsg_read(struct log_buffer *log_b, struct 
file *file,
}
 
raw_spin_unlock_irq(_b->lock);
-   ret = wait_event_interruptible(log_b->wait,
-  user->seq != log_b->next_seq);
+   if (log_b == _buf) {
+   ret = wait_event_interruptible(log_b->wait,
+   user->seq != log_b->next_seq);
+   } else {
+   rcu_read_unlock();
+   kref_get(_b->refcount);
+   ret = wait_event_interruptible(log_b->wait,
+   user->seq != log_b->next_seq);
+   if (log_b->minor == -1)
+   ret = -ENXIO;
+   if (kref_put(_b->refcount, log_buf_release))
+   ret = -ENXIO;
+   rcu_read_lock();
+   }
if (ret)
goto out;
raw_spin_lock_irq(_b->lock);
@@ -311,8 +324,14 @@ static unsigned int devkmsg_poll(struct file *file, 
poll_table *wait)
rcu_read_lock();
list_for_each_entry_rcu(log_b, _buf.list, list) {
if (log_b->minor == minor) {
+   kref_get(_b->refcount);
+   rcu_read_unlock();
+
ret = kmsg_poll(log_b, file, wait);
-   break;
+
+   if (kref_put(_b->refcount, log_buf_release))
+   return POLLERR|POLLNVAL;
+   return ret;
}
}
rcu_read_unlock();
@@ -428,6 +447,88 @@ int kmsg_mode(int minor, umode_t *mode)
return ret;
 }
 
+static DEFINE_SPINLOCK(kmsg_sys_list_lock);
+
+int kmsg_sys_buffer_add(size_t size, umode_t mode)
+{
+   unsigned long flags;
+   int minor = log_buf.minor;
+   struct log_buffer *log_b;
+   struct log_buffer *log_b_new;
+
+   if (size < LOG_LINE_MAX + PREFIX_MAX)
+   return -EINVAL;
+
+   log_b_new = kzalloc(sizeof(struct log_buffer), GFP_KERNEL);
+   if (!log_b_new)
+   return -ENOMEM;
+
+   log_b_new->buf = kmalloc(size, GFP_KERNEL);
+   if (!log_b_new->buf) {
+   kfree(log_b_new);
+   return -ENOMEM;
+   }
+
+   log_b_new->len = size;
+   log_b_new->lock = __RAW_SPIN_LOCK_UNLOCKED(log_b_new->lock);
+   init_waitqueue_head(_b_new->wait);
+   kref_init(_b_new->refcount);
+   log_b_new->mode = mode;
+
+   kref_get(_b_new->refcount);
+
+   spin_lock_irqsave(_sys_list_lock, flags);
+
+   list_for_each_entry(log_b, _buf.list, list) {
+   if (log_b->minor - minor > 1)
+   break;
+
+   minor = log_b->minor;
+   }
+
+   if (!(minor & MINORMASK)) {
+   kref_put(_b->refcount, log_buf_release);
+   spin_unlock_irqrestore(_sys_list_lock, flags);
+   return -ERANGE;
+   }
+
+   minor += 1;
+   log_b_new->minor = minor;
+
+   list_add_tail_rcu(_b_new->list, _b->list);
+
+   spin_unlock_irqrestore(_sys_list_lock, flags);
+
+   return minor;
+}
+
+void kmsg_sys_buffer_del(int minor)
+{
+   unsigned long flags;
+   struct log_buffer *log_b;
+
+   

[RFC v4 5/9] kmsg: add function for adding and deleting additional buffers

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

Additional kmsg buffers should be created and deleted dynamically.

Adding two functions
* kmsg_sys_buffer_add() creates additional kmsg buffer returning minor
* kmsg_sys_buffer_del() deletes one based on provided minor

Signed-off-by: Marcin Niesluchowski 
Signed-off-by: Paul Osmialowski 
---
 include/linux/printk.h |   9 +
 kernel/printk/kmsg.c   | 107 +++--
 kernel/printk/printk.c |  12 ++
 kernel/printk/printk.h |   4 ++
 4 files changed, 129 insertions(+), 3 deletions(-)

diff --git a/include/linux/printk.h b/include/linux/printk.h
index 0c4f9de..513fa6f 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -431,6 +431,8 @@ extern const struct file_operations kmsg_fops;
 extern struct device *init_kmsg(int minor, umode_t mode);
 extern int kmsg_memory_open(struct inode *inode, struct file *filp);
 extern int kmsg_mode(int minor, umode_t *mode);
+extern int kmsg_sys_buffer_add(size_t size, umode_t mode);
+extern void kmsg_sys_buffer_del(int minor);
 
 #else
 
@@ -449,6 +451,13 @@ static inline int kmsg_mode(int minor, umode_t *mode)
return -ENXIO;
 }
 
+static inline int kmsg_sys_buffer_add(size_t size, umode_t mode)
+{
+   return -ENXIO;
+}
+
+static inline void kmsg_sys_buffer_del(int minor) {}
+
 #endif
 
 enum {
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 726250f..9222fdc 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -19,6 +19,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 
@@ -141,8 +142,20 @@ static ssize_t kmsg_read(struct log_buffer *log_b, struct 
file *file,
}
 
raw_spin_unlock_irq(_b->lock);
-   ret = wait_event_interruptible(log_b->wait,
-  user->seq != log_b->next_seq);
+   if (log_b == _buf) {
+   ret = wait_event_interruptible(log_b->wait,
+   user->seq != log_b->next_seq);
+   } else {
+   rcu_read_unlock();
+   kref_get(_b->refcount);
+   ret = wait_event_interruptible(log_b->wait,
+   user->seq != log_b->next_seq);
+   if (log_b->minor == -1)
+   ret = -ENXIO;
+   if (kref_put(_b->refcount, log_buf_release))
+   ret = -ENXIO;
+   rcu_read_lock();
+   }
if (ret)
goto out;
raw_spin_lock_irq(_b->lock);
@@ -311,8 +324,14 @@ static unsigned int devkmsg_poll(struct file *file, 
poll_table *wait)
rcu_read_lock();
list_for_each_entry_rcu(log_b, _buf.list, list) {
if (log_b->minor == minor) {
+   kref_get(_b->refcount);
+   rcu_read_unlock();
+
ret = kmsg_poll(log_b, file, wait);
-   break;
+
+   if (kref_put(_b->refcount, log_buf_release))
+   return POLLERR|POLLNVAL;
+   return ret;
}
}
rcu_read_unlock();
@@ -428,6 +447,88 @@ int kmsg_mode(int minor, umode_t *mode)
return ret;
 }
 
+static DEFINE_SPINLOCK(kmsg_sys_list_lock);
+
+int kmsg_sys_buffer_add(size_t size, umode_t mode)
+{
+   unsigned long flags;
+   int minor = log_buf.minor;
+   struct log_buffer *log_b;
+   struct log_buffer *log_b_new;
+
+   if (size < LOG_LINE_MAX + PREFIX_MAX)
+   return -EINVAL;
+
+   log_b_new = kzalloc(sizeof(struct log_buffer), GFP_KERNEL);
+   if (!log_b_new)
+   return -ENOMEM;
+
+   log_b_new->buf = kmalloc(size, GFP_KERNEL);
+   if (!log_b_new->buf) {
+   kfree(log_b_new);
+   return -ENOMEM;
+   }
+
+   log_b_new->len = size;
+   log_b_new->lock = __RAW_SPIN_LOCK_UNLOCKED(log_b_new->lock);
+   init_waitqueue_head(_b_new->wait);
+   kref_init(_b_new->refcount);
+   log_b_new->mode = mode;
+
+   kref_get(_b_new->refcount);
+
+   spin_lock_irqsave(_sys_list_lock, flags);
+
+   list_for_each_entry(log_b, _buf.list, list) {
+   if (log_b->minor - minor > 1)
+   break;
+
+   minor = log_b->minor;
+   }
+
+   if (!(minor & MINORMASK)) {
+   kref_put(_b->refcount, log_buf_release);
+   spin_unlock_irqrestore(_sys_list_lock, flags);
+   return -ERANGE;
+   }
+
+   minor += 1;
+   log_b_new->minor = minor;
+
+   list_add_tail_rcu(_b_new->list, _b->list);
+
+   spin_unlock_irqrestore(_sys_list_lock, flags);
+
+   return minor;
+}
+
+void kmsg_sys_buffer_del(int minor)
+{
+