[RFC v4 7/9] kmsg: add ioctl for kmsg* devices operating on buffers

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

There is no possibility to clear additional kmsg buffers,
get size of them or know what size should be passed to read
file operation (too small size causes it to retrun -EINVAL).

Add following ioctls which solve those issues:
* KMSG_CMD_GET_BUF_SIZE
* KMSG_CMD_GET_READ_SIZE_MAX
* KMSG_CMD_CLEAR

Signed-off-by: Marcin Niesluchowski 
Signed-off-by: Paul Osmialowski 
---
 Documentation/ioctl/ioctl-number.txt |  2 +-
 include/uapi/linux/kmsg_ioctl.h  | 15 ++
 kernel/printk/kmsg.c | 57 ++--
 3 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/Documentation/ioctl/ioctl-number.txt 
b/Documentation/ioctl/ioctl-number.txt
index 76dec8b..d36bb04 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -319,7 +319,7 @@ Code  Seq#(hex) Include FileComments

 0xB1   00-1F   PPPoX   
 0xB3   00  linux/mmc/ioctl.h
-0xBB   00-02   uapi/linux/kmsg_ioctl.h
+0xBB   00-83   uapi/linux/kmsg_ioctl.h
 0xC0   00-0F   linux/usb/iowarrior.h
 0xCA   00-0F   uapi/misc/cxl.h
 0xCA   80-8F   uapi/scsi/cxlflash_ioctl.h
diff --git a/include/uapi/linux/kmsg_ioctl.h b/include/uapi/linux/kmsg_ioctl.h
index 96e7930..bfd9cd3 100644
--- a/include/uapi/linux/kmsg_ioctl.h
+++ b/include/uapi/linux/kmsg_ioctl.h
@@ -27,4 +27,19 @@ struct kmsg_cmd_buffer_add {
  struct kmsg_cmd_buffer_add)
 #define KMSG_CMD_BUFFER_DEL_IOW(KMSG_IOCTL_MAGIC, 0x01, int)
 
+/*
+ * A ioctl interface for kmsg* devices.
+ *
+ * KMSG_CMD_GET_BUF_SIZE:  Retrieve cyclic log buffer size associated with
+ * device.
+ * KMSG_CMD_GET_READ_SIZE_MAX: Retrieve max size of data read by kmsg read
+ * operation.
+ * KMSG_CMD_CLEAR: Clears cyclic log buffer. After that operation
+ * there is no data to read from buffer unless
+ * logs are written.
+ */
+#define KMSG_CMD_GET_BUF_SIZE  _IOR(KMSG_IOCTL_MAGIC, 0x80, __u32)
+#define KMSG_CMD_GET_READ_SIZE_MAX _IOR(KMSG_IOCTL_MAGIC, 0x81, __u32)
+#define KMSG_CMD_CLEAR _IO(KMSG_IOCTL_MAGIC, 0x82)
+
 #endif
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 62bb4d5..bcf0801 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -247,8 +247,9 @@ static loff_t kmsg_llseek(struct log_buffer *log_b, struct 
file *file,
}
/*
 * The first record after the last SYSLOG_ACTION_CLEAR,
-* like issued by 'dmesg -c'. Reading /dev/kmsg itself
-* changes no global state, and does not clear anything.
+* like issued by 'dmesg -c' or KMSG_CMD_CLEAR ioctl
+* command. Reading /dev/kmsg itself changes no global
+* state, and does not clear anything.
 */
user->idx = log_b->clear_idx;
user->seq = log_b->clear_seq;
@@ -391,6 +392,56 @@ static int devkmsg_open(struct inode *inode, struct file 
*file)
return ret;
 }
 
+static long kmsg_ioctl(struct log_buffer *log_b, unsigned int cmd,
+  unsigned long arg)
+{
+   void __user *argp = (void __user *)arg;
+   static const u32 read_size_max = CONSOLE_EXT_LOG_MAX;
+
+   switch (cmd) {
+   case KMSG_CMD_GET_BUF_SIZE:
+   if (copy_to_user(argp, _b->len, sizeof(u32)))
+   return -EFAULT;
+   break;
+   case KMSG_CMD_GET_READ_SIZE_MAX:
+   if (copy_to_user(argp, _size_max, sizeof(u32)))
+   return -EFAULT;
+   break;
+   case KMSG_CMD_CLEAR:
+   if (!capable(CAP_SYSLOG))
+   return -EPERM;
+   raw_spin_lock_irq(_b->lock);
+   log_b->clear_seq = log_b->next_seq;
+   log_b->clear_idx = log_b->next_idx;
+   raw_spin_unlock_irq(_b->lock);
+   break;
+   default:
+   return -ENOTTY;
+   }
+   return 0;
+}
+
+static long devkmsg_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+   long ret = -ENXIO;
+   int minor = iminor(file->f_inode);
+   struct log_buffer *log_b;
+
+   if (minor == log_buf.minor)
+   return kmsg_ioctl(_buf, cmd, arg);
+
+   rcu_read_lock();
+   list_for_each_entry_rcu(log_b, _buf.list, list) {
+   if (log_b->minor == minor) {
+   ret = kmsg_ioctl(log_b, cmd, arg);
+   break;
+   }
+   }
+   rcu_read_unlock();
+   return ret;
+}
+
 static int devkmsg_release(struct inode *inode, struct file *file)
 {
struct devkmsg_user *user = 

[RFC v4 7/9] kmsg: add ioctl for kmsg* devices operating on buffers

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

There is no possibility to clear additional kmsg buffers,
get size of them or know what size should be passed to read
file operation (too small size causes it to retrun -EINVAL).

Add following ioctls which solve those issues:
* KMSG_CMD_GET_BUF_SIZE
* KMSG_CMD_GET_READ_SIZE_MAX
* KMSG_CMD_CLEAR

Signed-off-by: Marcin Niesluchowski 
Signed-off-by: Paul Osmialowski 
---
 Documentation/ioctl/ioctl-number.txt |  2 +-
 include/uapi/linux/kmsg_ioctl.h  | 15 ++
 kernel/printk/kmsg.c | 57 ++--
 3 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/Documentation/ioctl/ioctl-number.txt 
b/Documentation/ioctl/ioctl-number.txt
index 76dec8b..d36bb04 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -319,7 +319,7 @@ Code  Seq#(hex) Include FileComments

 0xB1   00-1F   PPPoX   
 0xB3   00  linux/mmc/ioctl.h
-0xBB   00-02   uapi/linux/kmsg_ioctl.h
+0xBB   00-83   uapi/linux/kmsg_ioctl.h
 0xC0   00-0F   linux/usb/iowarrior.h
 0xCA   00-0F   uapi/misc/cxl.h
 0xCA   80-8F   uapi/scsi/cxlflash_ioctl.h
diff --git a/include/uapi/linux/kmsg_ioctl.h b/include/uapi/linux/kmsg_ioctl.h
index 96e7930..bfd9cd3 100644
--- a/include/uapi/linux/kmsg_ioctl.h
+++ b/include/uapi/linux/kmsg_ioctl.h
@@ -27,4 +27,19 @@ struct kmsg_cmd_buffer_add {
  struct kmsg_cmd_buffer_add)
 #define KMSG_CMD_BUFFER_DEL_IOW(KMSG_IOCTL_MAGIC, 0x01, int)
 
+/*
+ * A ioctl interface for kmsg* devices.
+ *
+ * KMSG_CMD_GET_BUF_SIZE:  Retrieve cyclic log buffer size associated with
+ * device.
+ * KMSG_CMD_GET_READ_SIZE_MAX: Retrieve max size of data read by kmsg read
+ * operation.
+ * KMSG_CMD_CLEAR: Clears cyclic log buffer. After that operation
+ * there is no data to read from buffer unless
+ * logs are written.
+ */
+#define KMSG_CMD_GET_BUF_SIZE  _IOR(KMSG_IOCTL_MAGIC, 0x80, __u32)
+#define KMSG_CMD_GET_READ_SIZE_MAX _IOR(KMSG_IOCTL_MAGIC, 0x81, __u32)
+#define KMSG_CMD_CLEAR _IO(KMSG_IOCTL_MAGIC, 0x82)
+
 #endif
diff --git a/kernel/printk/kmsg.c b/kernel/printk/kmsg.c
index 62bb4d5..bcf0801 100644
--- a/kernel/printk/kmsg.c
+++ b/kernel/printk/kmsg.c
@@ -247,8 +247,9 @@ static loff_t kmsg_llseek(struct log_buffer *log_b, struct 
file *file,
}
/*
 * The first record after the last SYSLOG_ACTION_CLEAR,
-* like issued by 'dmesg -c'. Reading /dev/kmsg itself
-* changes no global state, and does not clear anything.
+* like issued by 'dmesg -c' or KMSG_CMD_CLEAR ioctl
+* command. Reading /dev/kmsg itself changes no global
+* state, and does not clear anything.
 */
user->idx = log_b->clear_idx;
user->seq = log_b->clear_seq;
@@ -391,6 +392,56 @@ static int devkmsg_open(struct inode *inode, struct file 
*file)
return ret;
 }
 
+static long kmsg_ioctl(struct log_buffer *log_b, unsigned int cmd,
+  unsigned long arg)
+{
+   void __user *argp = (void __user *)arg;
+   static const u32 read_size_max = CONSOLE_EXT_LOG_MAX;
+
+   switch (cmd) {
+   case KMSG_CMD_GET_BUF_SIZE:
+   if (copy_to_user(argp, _b->len, sizeof(u32)))
+   return -EFAULT;
+   break;
+   case KMSG_CMD_GET_READ_SIZE_MAX:
+   if (copy_to_user(argp, _size_max, sizeof(u32)))
+   return -EFAULT;
+   break;
+   case KMSG_CMD_CLEAR:
+   if (!capable(CAP_SYSLOG))
+   return -EPERM;
+   raw_spin_lock_irq(_b->lock);
+   log_b->clear_seq = log_b->next_seq;
+   log_b->clear_idx = log_b->next_idx;
+   raw_spin_unlock_irq(_b->lock);
+   break;
+   default:
+   return -ENOTTY;
+   }
+   return 0;
+}
+
+static long devkmsg_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+   long ret = -ENXIO;
+   int minor = iminor(file->f_inode);
+   struct log_buffer *log_b;
+
+   if (minor == log_buf.minor)
+   return kmsg_ioctl(_buf, cmd, arg);
+
+   rcu_read_lock();
+   list_for_each_entry_rcu(log_b, _buf.list, list) {
+   if (log_b->minor == minor) {
+   ret = kmsg_ioctl(log_b, cmd, arg);
+   break;
+   }
+   }
+   rcu_read_unlock();
+   return ret;
+}
+
 static int devkmsg_release(struct