[RFC v4 5/9] kmsg: add function for adding and deleting additional buffers
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
From: Marcin NiesluchowskiAdditional 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) +{ +