summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKay Sievers <kay@vrfy.org>2012-04-06 20:12:30 +0200
committerKay Sievers <kay@vrfy.org>2012-04-06 20:12:30 +0200
commitf260f8f17e9dcd610b119c725846c838943e197a (patch)
tree3a1599225c57dbbc55d2ea40cdff59e606265f5c
parent86c1cbd04cf88fae08303c55baf364db9698d8bf (diff)
downloadpatches-f260f8f17e9dcd610b119c725846c838943e197a.tar.gz
split out record-buffer patch
-rw-r--r--printk-devkmsg.patch505
-rw-r--r--printk-records.patch (renamed from printk.patch)564
-rw-r--r--series3
3 files changed, 596 insertions, 476 deletions
diff --git a/printk-devkmsg.patch b/printk-devkmsg.patch
new file mode 100644
index 0000000..a7fa795
--- /dev/null
+++ b/printk-devkmsg.patch
@@ -0,0 +1,505 @@
+---
+ drivers/base/core.c | 52 +++++++-
+ drivers/char/mem.c | 61 ---------
+ include/linux/printk.h | 2
+ kernel/printk.c | 313 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 365 insertions(+), 63 deletions(-)
+
+--- a/drivers/base/core.c
++++ b/drivers/base/core.c
+@@ -25,6 +25,7 @@
+ #include <linux/mutex.h>
+ #include <linux/async.h>
+ #include <linux/pm_runtime.h>
++#include <linux/netdevice.h>
+
+ #include "base.h"
+ #include "power/power.h"
+@@ -1843,15 +1844,60 @@ void device_shutdown(void)
+ */
+
+ #ifdef CONFIG_PRINTK
+-
+ int __dev_printk(const char *level, const struct device *dev,
+ struct va_format *vaf)
+ {
++ char dict[128];
++ size_t dictlen = 0;
++ const char *subsys;
++
+ if (!dev)
+ return printk("%s(NULL device *): %pV", level, vaf);
+
+- return printk("%s%s %s: %pV",
+- level, dev_driver_string(dev), dev_name(dev), vaf);
++ if (dev->class)
++ subsys = dev->class->name;
++ else if (dev->bus)
++ subsys = dev->bus->name;
++ else
++ subsys = "(NULL subsystem)";
++
++ dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
++ "SUBSYSTEM=%s", subsys);
++
++ /*
++ * Add device identifier DEVICE=:
++ * b12:8 block dev_t
++ * c127:3 char dev_t
++ * n8 netdev ifindex
++ * +sound:card0 subsystem:devname
++ */
++ if (MAJOR(dev->devt)) {
++ char c;
++
++ if (strcmp(subsys, "block") == 0)
++ c = 'b';
++ else
++ c = 'c';
++ dictlen++;
++ dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
++ "DEVICE=%c%u:%u",
++ c, MAJOR(dev->devt), MINOR(dev->devt));
++ } else if (strcmp(subsys, "net") == 0) {
++ struct net_device *net = to_net_dev(dev);
++
++ dictlen++;
++ dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
++ "DEVICE=n%u", net->ifindex);
++ } else {
++ dictlen++;
++ dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
++ "DEVICE=+%s:%s", subsys, dev_name(dev));
++ }
++
++ return printk_emit(0, level[1] - '0',
++ dict, dictlen,
++ "%s %s: %pV",
++ dev_driver_string(dev), dev_name(dev), vaf);
+ }
+ EXPORT_SYMBOL(__dev_printk);
+
+--- a/drivers/char/mem.c
++++ b/drivers/char/mem.c
+@@ -807,65 +807,6 @@ static const struct file_operations oldm
+ };
+ #endif
+
+-static ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv,
+- unsigned long count, loff_t pos)
+-{
+- char *buf, *line;
+- int i;
+- int level = default_message_loglevel;
+- int facility = 1; /* LOG_USER */
+- size_t len = iov_length(iv, count);
+- ssize_t ret = len;
+-
+- if (len > 1024)
+- return -EINVAL;
+- buf = kmalloc(len+1, GFP_KERNEL);
+- if (buf == NULL)
+- return -ENOMEM;
+-
+- line = buf;
+- for (i = 0; i < count; i++) {
+- if (copy_from_user(line, iv[i].iov_base, iv[i].iov_len))
+- goto out;
+- line += iv[i].iov_len;
+- }
+-
+- /*
+- * Extract and skip the syslog prefix <[0-9]*>. Coming from userspace
+- * the decimal value represents 32bit, the lower 3 bit are the log
+- * level, the rest are the log facility.
+- *
+- * If no prefix or no userspace facility is specified, we
+- * enforce LOG_USER, to be able to reliably distinguish
+- * kernel-generated messages from userspace-injected ones.
+- */
+- line = buf;
+- if (line[0] == '<') {
+- char *endp = NULL;
+-
+- i = simple_strtoul(line+1, &endp, 10);
+- if (endp && endp[0] == '>') {
+- level = i & 7;
+- if (i >> 3)
+- facility = i >> 3;
+- endp++;
+- len -= endp - line;
+- line = endp;
+- }
+- }
+- line[len] = '\0';
+-
+- printk_emit(facility, level, NULL, 0, "%s", line);
+-out:
+- kfree(buf);
+- return ret;
+-}
+-
+-static const struct file_operations kmsg_fops = {
+- .aio_write = kmsg_writev,
+- .llseek = noop_llseek,
+-};
+-
+ static const struct memdev {
+ const char *name;
+ umode_t mode;
+@@ -884,7 +825,7 @@ static const struct memdev {
+ [7] = { "full", 0666, &full_fops, NULL },
+ [8] = { "random", 0666, &random_fops, NULL },
+ [9] = { "urandom", 0666, &urandom_fops, NULL },
+- [11] = { "kmsg", 0, &kmsg_fops, NULL },
++ [11] = { "kmsg", 0644, &kmsg_fops, NULL },
+ #ifdef CONFIG_CRASH_DUMP
+ [12] = { "oldmem", 0, &oldmem_fops, NULL },
+ #endif
+--- a/include/linux/printk.h
++++ b/include/linux/printk.h
+@@ -300,6 +300,8 @@ extern void dump_stack(void) __cold;
+ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+ #endif
+
++extern const struct file_operations kmsg_fops;
++
+ enum {
+ DUMP_PREFIX_NONE,
+ DUMP_PREFIX_ADDRESS,
+--- a/kernel/printk.c
++++ b/kernel/printk.c
+@@ -41,6 +41,7 @@
+ #include <linux/cpu.h>
+ #include <linux/notifier.h>
+ #include <linux/rculist.h>
++#include <linux/poll.h>
+
+ #include <asm/uaccess.h>
+
+@@ -149,6 +150,48 @@ static int console_may_schedule;
+ * length of the message text is stored in the header, the stored message
+ * is not terminated.
+ *
++ * Optionally, a message can carry a dictionary of properties (key/value pairs),
++ * to provide userspace with a machine-readable message context.
++ *
++ * Examples for well-defined, commonly used property names are:
++ * DEVICE=b12:8 device identifier
++ * b12:8 block dev_t
++ * c127:3 char dev_t
++ * n8 netdev ifindex
++ * +sound:card0 subsystem:devname
++ * SUBSYSTEM=pci driver-core subsystem name
++ *
++ * Valid characters in property names are [a-zA-Z0-9.-_]. The plain text value
++ * follows directly after a '=' character. Every property is terminated by
++ * a '\0' character. The last property is not terminated.
++ *
++ * Example of a message structure:
++ * 0000 ff 8f 00 00 00 00 00 00 monotonic time in nsec
++ * 0008 34 00 record is 52 bytes long
++ * 000a 0b 00 text is 11 bytes long
++ * 000c 1f 00 dictionary is 23 bytes long
++ * 000e 03 00 LOG_KERN (facility) LOG_ERR (level)
++ * 0010 69 74 27 73 20 61 20 6c "it's a l"
++ * 69 6e 65 "ine"
++ * 001b 44 45 56 49 43 "DEVIC"
++ * 45 3d 62 38 3a 32 00 44 "E=b8:2\0D"
++ * 52 49 56 45 52 3d 62 75 "RIVER=bu"
++ * 67 "g"
++ * 0032 00 00 00 padding to next message header
++ *
++ * The 'struct log' buffer header must never be directly exported to
++ * userspace, it is a kernel-private implementation detail that might
++ * need to be changed in the future, when the requirements change.
++ *
++ * /dev/kmsg exports the structured data in the following line format:
++ * "level,sequnum,timestamp;<message text>\n"
++ *
++ * The optional key/value pairs are attached as continuation lines starting
++ * with a space character and terminated by a newline. All possible
++ * non-prinatable characters are escaped in the "\xff" notation.
++ *
++ * Users of the export format should ignore possible additional values
++ * separated by ',', and find the message after the ';' character.
+ */
+
+ struct log {
+@@ -297,6 +340,276 @@ static void log_store(int facility, int
+ log_next_seq++;
+ }
+
++/* /dev/kmsg - userspace message inject/listen interface */
++struct devkmsg_user {
++ u64 seq;
++ u32 idx;
++ struct mutex lock;
++ char buf[8192];
++};
++
++static ssize_t devkmsg_writev(struct kiocb *iocb, const struct iovec *iv,
++ unsigned long count, loff_t pos)
++{
++ char *buf, *line;
++ int i;
++ int level = default_message_loglevel;
++ int facility = 1; /* LOG_USER */
++ size_t len = iov_length(iv, count);
++ ssize_t ret = len;
++
++ if (len > LOG_LINE_MAX)
++ return -EINVAL;
++ buf = kmalloc(len+1, GFP_KERNEL);
++ if (buf == NULL)
++ return -ENOMEM;
++
++ line = buf;
++ for (i = 0; i < count; i++) {
++ if (copy_from_user(line, iv[i].iov_base, iv[i].iov_len))
++ goto out;
++ line += iv[i].iov_len;
++ }
++
++ /*
++ * Extract and skip the syslog prefix <[0-9]*>. Coming from userspace
++ * the decimal value represents 32bit, the lower 3 bit are the log
++ * level, the rest are the log facility.
++ *
++ * If no prefix or no userspace facility is specified, we
++ * enforce LOG_USER, to be able to reliably distinguish
++ * kernel-generated messages from userspace-injected ones.
++ */
++ line = buf;
++ if (line[0] == '<') {
++ char *endp = NULL;
++
++ i = simple_strtoul(line+1, &endp, 10);
++ if (endp && endp[0] == '>') {
++ level = i & 7;
++ if (i >> 3)
++ facility = i >> 3;
++ endp++;
++ len -= endp - line;
++ line = endp;
++ }
++ }
++ line[len] = '\0';
++
++ printk_emit(facility, level, NULL, 0, "%s", line);
++out:
++ kfree(buf);
++ return ret;
++}
++
++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 log *msg;
++ size_t i;
++ size_t len;
++ ssize_t ret;
++
++ if (!user)
++ return -EBADF;
++
++ mutex_lock(&user->lock);
++ raw_spin_lock(&logbuf_lock);
++ while (user->seq == log_next_seq) {
++ if (file->f_flags & O_NONBLOCK) {
++ ret = -EAGAIN;
++ raw_spin_unlock(&logbuf_lock);
++ goto out;
++ }
++
++ raw_spin_unlock(&logbuf_lock);
++ ret = wait_event_interruptible(log_wait,
++ user->seq != log_next_seq);
++ if (ret)
++ goto out;
++ raw_spin_lock(&logbuf_lock);
++ }
++
++ 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;
++ ret = -EPIPE;
++ raw_spin_unlock(&logbuf_lock);
++ goto out;
++ }
++
++ msg = log_from_idx(user->idx);
++ len = sprintf(user->buf, "%u,%llu,%llu;",
++ msg->level, user->seq, msg->ts_nsec / 1000);
++
++ /* escape non-printable characters */
++ for (i = 0; i < msg->text_len; i++) {
++ char c = log_text(msg)[i];
++
++ if (c < ' ' || c >= 128)
++ len += sprintf(user->buf + len, "\\x%02x", c);
++ else
++ user->buf[len++] = c;
++ }
++ user->buf[len++] = '\n';
++
++ if (msg->dict_len) {
++ bool line = true;
++
++ for (i = 0; i < msg->dict_len; i++) {
++ char c = log_dict(msg)[i];
++
++ if (line) {
++ user->buf[len++] = ' ';
++ line = false;
++ }
++
++ if (c == '\0') {
++ user->buf[len++] = '\n';
++ line = true;
++ continue;
++ }
++
++ if (c < ' ' || c >= 128) {
++ len += sprintf(user->buf + len, "\\x%02x", c);
++ continue;
++ }
++
++ user->buf[len++] = c;
++ }
++ user->buf[len++] = '\n';
++ }
++
++ user->idx = log_next(user->idx);
++ user->seq++;
++ raw_spin_unlock(&logbuf_lock);
++
++ if (len > count) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (copy_to_user(buf, user->buf, len)) {
++ ret = -EFAULT;
++ goto out;
++ }
++ ret = len;
++out:
++ mutex_unlock(&user->lock);
++ return ret;
++}
++
++static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
++{
++ struct devkmsg_user *user = file->private_data;
++ loff_t ret = 0;
++
++ if (!user)
++ return -EBADF;
++ if (offset)
++ return -ESPIPE;
++
++ raw_spin_lock(&logbuf_lock);
++ switch (whence) {
++ case SEEK_SET:
++ /* the first record */
++ user->idx = log_first_idx;
++ user->seq = log_first_seq;
++ break;
++ case SEEK_DATA:
++ /*
++ * 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.
++ */
++ user->idx = clear_idx;
++ user->seq = clear_seq;
++ break;
++ case SEEK_END:
++ /* after the last record */
++ user->idx = log_next_idx;
++ user->seq = log_next_seq;
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ raw_spin_unlock(&logbuf_lock);
++ return ret;
++}
++
++static unsigned int devkmsg_poll(struct file *file, poll_table *wait)
++{
++ struct devkmsg_user *user = file->private_data;
++ int ret = 0;
++
++ if (!user)
++ return POLLERR|POLLNVAL;
++
++ poll_wait(file, &log_wait, wait);
++
++ raw_spin_lock(&logbuf_lock);
++ if (user->seq < log_next_seq) {
++ /* return error when data has vanished underneath us */
++ if (user->seq < log_first_seq)
++ ret = POLLIN|POLLRDNORM|POLLERR|POLLPRI;
++ ret = POLLIN|POLLRDNORM;
++ }
++ raw_spin_unlock(&logbuf_lock);
++
++ return ret;
++}
++
++static int devkmsg_open(struct inode *inode, struct file *file)
++{
++ struct devkmsg_user *user;
++ int err;
++
++ /* write-only does not need any file context */
++ if ((file->f_flags & O_ACCMODE) == O_WRONLY)
++ return 0;
++
++ err = security_syslog(SYSLOG_ACTION_READ_ALL);
++ if (err)
++ return err;
++
++ user = kmalloc(sizeof(struct devkmsg_user), GFP_KERNEL);
++ if (!user)
++ return -ENOMEM;
++
++ mutex_init(&user->lock);
++
++ raw_spin_lock(&logbuf_lock);
++ user->idx = log_first_idx;
++ user->seq = log_first_seq;
++ raw_spin_unlock(&logbuf_lock);
++
++ file->private_data = user;
++ return 0;
++}
++
++static int devkmsg_release(struct inode *inode, struct file *file)
++{
++ struct devkmsg_user *user = file->private_data;
++
++ if (!user)
++ return 0;
++
++ mutex_destroy(&user->lock);
++ kfree(user);
++ return 0;
++}
++
++const struct file_operations kmsg_fops = {
++ .open = devkmsg_open,
++ .read = devkmsg_read,
++ .aio_write = devkmsg_writev,
++ .llseek = devkmsg_llseek,
++ .poll = devkmsg_poll,
++ .release = devkmsg_release,
++};
++
+ #ifdef CONFIG_KEXEC
+ /*
+ * This appends the listed symbols to /proc/vmcoreinfo
diff --git a/printk.patch b/printk-records.patch
index 0938cea..50c0f50 100644
--- a/printk.patch
+++ b/printk-records.patch
@@ -100,142 +100,85 @@ Various features of this patch:
Tested-by: William Douglas <william.douglas@intel.com>
Signed-off-by: Kay Sievers <kay@vrfy.org>
---
- drivers/base/core.c | 52 +
- drivers/char/mem.c | 40 -
- include/linux/printk.h | 13
- kernel/printk.c | 1333 +++++++++++++++++++++++++++++++++----------------
- 4 files changed, 969 insertions(+), 469 deletions(-)
+ drivers/char/mem.c | 55 +-
+ include/linux/printk.h | 11
+ kernel/printk.c | 1020 ++++++++++++++++++++++++++++---------------------
+ 3 files changed, 642 insertions(+), 444 deletions(-)
---- a/drivers/base/core.c
-+++ b/drivers/base/core.c
-@@ -25,6 +25,7 @@
- #include <linux/mutex.h>
- #include <linux/async.h>
- #include <linux/pm_runtime.h>
-+#include <linux/netdevice.h>
-
- #include "base.h"
- #include "power/power.h"
-@@ -1843,15 +1844,60 @@ void device_shutdown(void)
- */
-
- #ifdef CONFIG_PRINTK
--
- int __dev_printk(const char *level, const struct device *dev,
- struct va_format *vaf)
- {
-+ char dict[128];
-+ size_t dictlen = 0;
-+ const char *subsys;
-+
- if (!dev)
- return printk("%s(NULL device *): %pV", level, vaf);
-
-- return printk("%s%s %s: %pV",
-- level, dev_driver_string(dev), dev_name(dev), vaf);
-+ if (dev->class)
-+ subsys = dev->class->name;
-+ else if (dev->bus)
-+ subsys = dev->bus->name;
-+ else
-+ subsys = "(NULL subsystem)";
-+
-+ dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
-+ "SUBSYSTEM=%s", subsys);
-+
-+ /*
-+ * Add device identifier DEVICE=:
-+ * b12:8 block dev_t
-+ * c127:3 char dev_t
-+ * n8 netdev ifindex
-+ * +sound:card0 subsystem:devname
-+ */
-+ if (MAJOR(dev->devt)) {
-+ char c;
-+
-+ if (strcmp(subsys, "block") == 0)
-+ c = 'b';
-+ else
-+ c = 'c';
-+ dictlen++;
-+ dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
-+ "DEVICE=%c%u:%u",
-+ c, MAJOR(dev->devt), MINOR(dev->devt));
-+ } else if (strcmp(subsys, "net") == 0) {
-+ struct net_device *net = to_net_dev(dev);
-+
-+ dictlen++;
-+ dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
-+ "DEVICE=n%u", net->ifindex);
-+ } else {
-+ dictlen++;
-+ dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
-+ "DEVICE=+%s:%s", subsys, dev_name(dev));
-+ }
-+
-+ return printk_emit(0, level[1] - '0',
-+ dict, dictlen,
-+ "%s %s: %pV",
-+ dev_driver_string(dev), dev_name(dev), vaf);
- }
- EXPORT_SYMBOL(__dev_printk);
-
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
-@@ -807,44 +807,6 @@ static const struct file_operations oldm
- };
- #endif
-
--static ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv,
-- unsigned long count, loff_t pos)
--{
+@@ -810,33 +810,54 @@ static const struct file_operations oldm
+ static ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv,
+ unsigned long count, loff_t pos)
+ {
- char *line, *p;
-- int i;
++ char *buf, *line;
+ int i;
- ssize_t ret = -EFAULT;
-- size_t len = iov_length(iv, count);
--
++ int level = default_message_loglevel;
++ int facility = 1; /* LOG_USER */
+ size_t len = iov_length(iv, count);
++ ssize_t ret = len;
+
- line = kmalloc(len + 1, GFP_KERNEL);
- if (line == NULL)
-- return -ENOMEM;
--
++ if (len > 1024)
++ return -EINVAL;
++ buf = kmalloc(len+1, GFP_KERNEL);
++ if (buf == NULL)
+ return -ENOMEM;
+
- /*
- * copy all vectors into a single string, to ensure we do
- * not interleave our log line with other printk calls
- */
- p = line;
-- for (i = 0; i < count; i++) {
++ line = buf;
+ for (i = 0; i < count; i++) {
- if (copy_from_user(p, iv[i].iov_base, iv[i].iov_len))
-- goto out;
++ if (copy_from_user(line, iv[i].iov_base, iv[i].iov_len))
+ goto out;
- p += iv[i].iov_len;
-- }
++ line += iv[i].iov_len;
++ }
++
++ /*
++ * Extract and skip the syslog prefix <[0-9]*>. Coming from userspace
++ * the decimal value represents 32bit, the lower 3 bit are the log
++ * level, the rest are the log facility.
++ *
++ * If no prefix or no userspace facility is specified, we
++ * enforce LOG_USER, to be able to reliably distinguish
++ * kernel-generated messages from userspace-injected ones.
++ */
++ line = buf;
++ if (line[0] == '<') {
++ char *endp = NULL;
++
++ i = simple_strtoul(line+1, &endp, 10);
++ if (endp && endp[0] == '>') {
++ level = i & 7;
++ if (i >> 3)
++ facility = i >> 3;
++ endp++;
++ len -= endp - line;
++ line = endp;
++ }
+ }
- p[0] = '\0';
--
++ line[len] = '\0';
+
- ret = printk("%s", line);
- /* printk can add a prefix */
- if (ret > len)
- ret = len;
--out:
++ printk_emit(facility, level, NULL, 0, "%s", line);
+ out:
- kfree(line);
-- return ret;
--}
--
--static const struct file_operations kmsg_fops = {
-- .aio_write = kmsg_writev,
-- .llseek = noop_llseek,
--};
--
- static const struct memdev {
- const char *name;
- umode_t mode;
-@@ -863,7 +825,7 @@ static const struct memdev {
- [7] = { "full", 0666, &full_fops, NULL },
- [8] = { "random", 0666, &random_fops, NULL },
- [9] = { "urandom", 0666, &urandom_fops, NULL },
-- [11] = { "kmsg", 0, &kmsg_fops, NULL },
-+ [11] = { "kmsg", 0644, &kmsg_fops, NULL },
- #ifdef CONFIG_CRASH_DUMP
- [12] = { "oldmem", 0, &oldmem_fops, NULL },
- #endif
++ kfree(buf);
+ return ret;
+ }
+
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -95,8 +95,19 @@ extern int printk_needs_cpu(int cpu);
@@ -258,26 +201,9 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
asmlinkage __printf(1, 2) __cold
int printk(const char *fmt, ...);
-@@ -289,6 +300,8 @@ extern void dump_stack(void) __cold;
- no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
- #endif
-
-+extern const struct file_operations kmsg_fops;
-+
- enum {
- DUMP_PREFIX_NONE,
- DUMP_PREFIX_ADDRESS,
--- a/kernel/printk.c
+++ b/kernel/printk.c
-@@ -41,6 +41,7 @@
- #include <linux/cpu.h>
- #include <linux/notifier.h>
- #include <linux/rculist.h>
-+#include <linux/poll.h>
-
- #include <asm/uaccess.h>
-
-@@ -54,8 +55,6 @@ void asmlinkage __attribute__((weak)) ea
+@@ -54,8 +54,6 @@ void asmlinkage __attribute__((weak)) ea
{
}
@@ -286,7 +212,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
-@@ -99,24 +98,6 @@ EXPORT_SYMBOL_GPL(console_drivers);
+@@ -99,24 +97,6 @@ EXPORT_SYMBOL_GPL(console_drivers);
static int console_locked, console_suspended;
/*
@@ -311,7 +237,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
* If exclusive_console is non-NULL then only this console is to be printed to.
*/
static struct console *exclusive_console;
-@@ -146,12 +127,488 @@ EXPORT_SYMBOL(console_set_on_cmdline);
+@@ -146,12 +126,176 @@ EXPORT_SYMBOL(console_set_on_cmdline);
static int console_may_schedule;
#ifdef CONFIG_PRINTK
@@ -338,48 +264,6 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
+ * length of the message text is stored in the header, the stored message
+ * is not terminated.
+ *
-+ * Optionally, a message can carry a dictionary of properties (key/value pairs),
-+ * to provide userspace with a machine-readable message context.
-+ *
-+ * Examples for well-defined, commonly used property names are:
-+ * DEVICE=b12:8 device identifier
-+ * b12:8 block dev_t
-+ * c127:3 char dev_t
-+ * n8 netdev ifindex
-+ * +sound:card0 subsystem:devname
-+ * SUBSYSTEM=pci driver-core subsystem name
-+ *
-+ * Valid characters in property names are [a-zA-Z0-9.-_]. The plain text value
-+ * follows directly after a '=' character. Every property is terminated by
-+ * a '\0' character. The last property is not terminated.
-+ *
-+ * Example of a message structure:
-+ * 0000 ff 8f 00 00 00 00 00 00 monotonic time in nsec
-+ * 0008 34 00 record is 52 bytes long
-+ * 000a 0b 00 text is 11 bytes long
-+ * 000c 1f 00 dictionary is 23 bytes long
-+ * 000e 03 00 LOG_KERN (facility) LOG_ERR (level)
-+ * 0010 69 74 27 73 20 61 20 6c "it's a l"
-+ * 69 6e 65 "ine"
-+ * 001b 44 45 56 49 43 "DEVIC"
-+ * 45 3d 62 38 3a 32 00 44 "E=b8:2\0D"
-+ * 52 49 56 45 52 3d 62 75 "RIVER=bu"
-+ * 67 "g"
-+ * 0032 00 00 00 padding to next message header
-+ *
-+ * The 'struct log' buffer header must never be directly exported to
-+ * userspace, it is a kernel-private implementation detail that might
-+ * need to be changed in the future, when the requirements change.
-+ *
-+ * /dev/kmsg exports the structured data in the following line format:
-+ * "level,sequnum,timestamp;<message text>\n"
-+ *
-+ * The optional key/value pairs are attached as continuation lines starting
-+ * with a space character and terminated by a newline. All possible
-+ * non-prinatable characters are escaped in the "\xff" notation.
-+ *
-+ * Users of the export format should ignore possible additional values
-+ * separated by ',', and find the message after the ';' character.
+ */
+
+struct log {
@@ -389,7 +273,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
+ u16 dict_len; /* length of dictionary buffer */
+ u16 level; /* syslog level + facility */
+};
-
++
+/*
+ * The logbuf_lock protects kmsg buffer, indices, counters. It is also
+ * used in interesting ways to provide interlocking in console_unlock();
@@ -400,7 +284,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
+static volatile unsigned int logbuf_cpu = UINT_MAX;
+
+#define LOG_LINE_MAX 1024
-+
+
+/* record buffer */
+#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
static char __log_buf[__LOG_BUF_LEN];
@@ -530,280 +414,10 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
+ log_next_idx += msg->len;
+ log_next_seq++;
+}
-+
-+/* /dev/kmsg - userspace message inject/listen interface */
-+struct devkmsg_user {
-+ u64 seq;
-+ u32 idx;
-+ struct mutex lock;
-+ char buf[8192];
-+};
-+
-+static ssize_t devkmsg_writev(struct kiocb *iocb, const struct iovec *iv,
-+ unsigned long count, loff_t pos)
-+{
-+ char *buf, *line;
-+ int i;
-+ int level = default_message_loglevel;
-+ int facility = 1; /* LOG_USER */
-+ size_t len = iov_length(iv, count);
-+ ssize_t ret = len;
-+
-+ if (len > LOG_LINE_MAX)
-+ return -EINVAL;
-+ buf = kmalloc(len+1, GFP_KERNEL);
-+ if (buf == NULL)
-+ return -ENOMEM;
-+
-+ line = buf;
-+ for (i = 0; i < count; i++) {
-+ if (copy_from_user(line, iv[i].iov_base, iv[i].iov_len))
-+ goto out;
-+ line += iv[i].iov_len;
-+ }
-+
-+ /*
-+ * Extract and skip the syslog prefix <[0-9]*>. Coming from userspace
-+ * the decimal value represents 32bit, the lower 3 bit are the log
-+ * level, the rest are the log facility.
-+ *
-+ * If no prefix or no userspace facility is specified, we
-+ * enforce LOG_USER, to be able to reliably distinguish
-+ * kernel-generated messages from userspace-injected ones.
-+ */
-+ line = buf;
-+ if (line[0] == '<') {
-+ char *endp = NULL;
-+
-+ i = simple_strtoul(line+1, &endp, 10);
-+ if (endp && endp[0] == '>') {
-+ level = i & 7;
-+ if (i >> 3)
-+ facility = i >> 3;
-+ endp++;
-+ len -= endp - line;
-+ line = endp;
-+ }
-+ }
-+ line[len] = '\0';
-+
-+ printk_emit(facility, level, NULL, 0, "%s", line);
-+out:
-+ kfree(buf);
-+ return ret;
-+}
-+
-+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 log *msg;
-+ size_t i;
-+ size_t len;
-+ ssize_t ret;
-+
-+ if (!user)
-+ return -EBADF;
-+
-+ mutex_lock(&user->lock);
-+ raw_spin_lock(&logbuf_lock);
-+ while (user->seq == log_next_seq) {
-+ if (file->f_flags & O_NONBLOCK) {
-+ ret = -EAGAIN;
-+ raw_spin_unlock(&logbuf_lock);
-+ goto out;
-+ }
-+
-+ raw_spin_unlock(&logbuf_lock);
-+ ret = wait_event_interruptible(log_wait,
-+ user->seq != log_next_seq);
-+ if (ret)
-+ goto out;
-+ raw_spin_lock(&logbuf_lock);
-+ }
-+
-+ 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;
-+ ret = -EPIPE;
-+ raw_spin_unlock(&logbuf_lock);
-+ goto out;
-+ }
-+
-+ msg = log_from_idx(user->idx);
-+ len = sprintf(user->buf, "%u,%llu,%llu;",
-+ msg->level, user->seq, msg->ts_nsec / 1000);
-+
-+ /* escape non-printable characters */
-+ for (i = 0; i < msg->text_len; i++) {
-+ char c = log_text(msg)[i];
-+
-+ if (c < ' ' || c >= 128)
-+ len += sprintf(user->buf + len, "\\x%02x", c);
-+ else
-+ user->buf[len++] = c;
-+ }
-+ user->buf[len++] = '\n';
-+
-+ if (msg->dict_len) {
-+ bool line = true;
-+
-+ for (i = 0; i < msg->dict_len; i++) {
-+ char c = log_dict(msg)[i];
-+
-+ if (line) {
-+ user->buf[len++] = ' ';
-+ line = false;
-+ }
-+
-+ if (c == '\0') {
-+ user->buf[len++] = '\n';
-+ line = true;
-+ continue;
-+ }
-+
-+ if (c < ' ' || c >= 128) {
-+ len += sprintf(user->buf + len, "\\x%02x", c);
-+ continue;
-+ }
-+
-+ user->buf[len++] = c;
-+ }
-+ user->buf[len++] = '\n';
-+ }
-+
-+ user->idx = log_next(user->idx);
-+ user->seq++;
-+ raw_spin_unlock(&logbuf_lock);
-+
-+ if (len > count) {
-+ ret = -EINVAL;
-+ goto out;
-+ }
-+
-+ if (copy_to_user(buf, user->buf, len)) {
-+ ret = -EFAULT;
-+ goto out;
-+ }
-+ ret = len;
-+out:
-+ mutex_unlock(&user->lock);
-+ return ret;
-+}
-+
-+static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
-+{
-+ struct devkmsg_user *user = file->private_data;
-+ loff_t ret = 0;
-+
-+ if (!user)
-+ return -EBADF;
-+ if (offset)
-+ return -ESPIPE;
-+
-+ raw_spin_lock(&logbuf_lock);
-+ switch (whence) {
-+ case SEEK_SET:
-+ /* the first record */
-+ user->idx = log_first_idx;
-+ user->seq = log_first_seq;
-+ break;
-+ case SEEK_DATA:
-+ /*
-+ * 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.
-+ */
-+ user->idx = clear_idx;
-+ user->seq = clear_seq;
-+ break;
-+ case SEEK_END:
-+ /* after the last record */
-+ user->idx = log_next_idx;
-+ user->seq = log_next_seq;
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ }
-+ raw_spin_unlock(&logbuf_lock);
-+ return ret;
-+}
-+
-+static unsigned int devkmsg_poll(struct file *file, poll_table *wait)
-+{
-+ struct devkmsg_user *user = file->private_data;
-+ int ret = 0;
-+
-+ if (!user)
-+ return POLLERR|POLLNVAL;
-+
-+ poll_wait(file, &log_wait, wait);
-+
-+ raw_spin_lock(&logbuf_lock);
-+ if (user->seq < log_next_seq) {
-+ /* return error when data has vanished underneath us */
-+ if (user->seq < log_first_seq)
-+ ret = POLLIN|POLLRDNORM|POLLERR|POLLPRI;
-+ ret = POLLIN|POLLRDNORM;
-+ }
-+ raw_spin_unlock(&logbuf_lock);
-+
-+ return ret;
-+}
-+
-+static int devkmsg_open(struct inode *inode, struct file *file)
-+{
-+ struct devkmsg_user *user;
-+ int err;
-+
-+ /* write-only does not need any file context */
-+ if ((file->f_flags & O_ACCMODE) == O_WRONLY)
-+ return 0;
-+
-+ err = security_syslog(SYSLOG_ACTION_READ_ALL);
-+ if (err)
-+ return err;
-+
-+ user = kmalloc(sizeof(struct devkmsg_user), GFP_KERNEL);
-+ if (!user)
-+ return -ENOMEM;
-+
-+ mutex_init(&user->lock);
-+
-+ raw_spin_lock(&logbuf_lock);
-+ user->idx = log_first_idx;
-+ user->seq = log_first_seq;
-+ raw_spin_unlock(&logbuf_lock);
-+
-+ file->private_data = user;
-+ return 0;
-+}
-+
-+static int devkmsg_release(struct inode *inode, struct file *file)
-+{
-+ struct devkmsg_user *user = file->private_data;
-+
-+ if (!user)
-+ return 0;
-+
-+ mutex_destroy(&user->lock);
-+ kfree(user);
-+ return 0;
-+}
-+
-+const struct file_operations kmsg_fops = {
-+ .open = devkmsg_open,
-+ .read = devkmsg_read,
-+ .aio_write = devkmsg_writev,
-+ .llseek = devkmsg_llseek,
-+ .poll = devkmsg_poll,
-+ .release = devkmsg_release,
-+};
#ifdef CONFIG_KEXEC
/*
-@@ -165,9 +622,9 @@ static int saved_console_loglevel = -1;
+@@ -165,9 +309,9 @@ static int saved_console_loglevel = -1;
void log_buf_kexec_setup(void)
{
VMCOREINFO_SYMBOL(log_buf);
@@ -815,7 +429,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
}
#endif
-@@ -191,7 +648,6 @@ early_param("log_buf_len", log_buf_len_s
+@@ -191,7 +335,6 @@ early_param("log_buf_len", log_buf_len_s
void __init setup_log_buf(int early)
{
unsigned long flags;
@@ -823,7 +437,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
char *new_log_buf;
int free;
-@@ -219,20 +675,8 @@ void __init setup_log_buf(int early)
+@@ -219,20 +362,8 @@ void __init setup_log_buf(int early)
log_buf_len = new_log_buf_len;
log_buf = new_log_buf;
new_log_buf_len = 0;
@@ -846,7 +460,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
pr_info("log_buf_len: %d\n", log_buf_len);
-@@ -332,11 +776,165 @@ static int check_syslog_permissions(int
+@@ -332,11 +463,165 @@ static int check_syslog_permissions(int
return 0;
}
@@ -1015,7 +629,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
int error;
error = check_syslog_permissions(type, from_file);
-@@ -364,28 +962,14 @@ int do_syslog(int type, char __user *buf
+@@ -364,28 +649,14 @@ int do_syslog(int type, char __user *buf
goto out;
}
error = wait_event_interruptible(log_wait,
@@ -1047,7 +661,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
/* FALL THRU */
/* Read last kernel messages */
case SYSLOG_ACTION_READ_ALL:
-@@ -399,52 +983,11 @@ int do_syslog(int type, char __user *buf
+@@ -399,52 +670,11 @@ int do_syslog(int type, char __user *buf
error = -EFAULT;
goto out;
}
@@ -1102,7 +716,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
/* Disable logging to console */
case SYSLOG_ACTION_CONSOLE_OFF:
if (saved_console_loglevel == -1)
-@@ -472,7 +1015,33 @@ int do_syslog(int type, char __user *buf
+@@ -472,7 +702,33 @@ int do_syslog(int type, char __user *buf
break;
/* Number of chars in the log buffer */
case SYSLOG_ACTION_SIZE_UNREAD:
@@ -1137,7 +751,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
break;
/* Size of the log buffer */
case SYSLOG_ACTION_SIZE_BUFFER:
-@@ -501,29 +1070,11 @@ void kdb_syslog_data(char *syslog_data[4
+@@ -501,29 +757,11 @@ void kdb_syslog_data(char *syslog_data[4
{
syslog_data[0] = log_buf;
syslog_data[1] = log_buf + log_buf_len;
@@ -1169,7 +783,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
static bool __read_mostly ignore_loglevel;
static int __init ignore_loglevel_setup(char *str)
-@@ -540,142 +1091,33 @@ MODULE_PARM_DESC(ignore_loglevel, "ignor
+@@ -540,142 +778,33 @@ MODULE_PARM_DESC(ignore_loglevel, "ignor
"print all kernel messages to the console.");
/*
@@ -1331,7 +945,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
}
/*
-@@ -700,16 +1142,6 @@ static void zap_locks(void)
+@@ -700,16 +829,6 @@ static void zap_locks(void)
sema_init(&console_sem, 1);
}
@@ -1348,7 +962,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
/* Check if we have any console registered that can be called early in boot. */
static int have_callable_console(void)
{
-@@ -722,51 +1154,6 @@ static int have_callable_console(void)
+@@ -722,51 +841,6 @@ static int have_callable_console(void)
return 0;
}
@@ -1400,7 +1014,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
/*
* Can we actually use the console at this time on this cpu?
*
-@@ -810,17 +1197,12 @@ static int console_trylock_for_printk(un
+@@ -810,17 +884,12 @@ static int console_trylock_for_printk(un
retval = 0;
}
}
@@ -1419,7 +1033,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
int printk_delay_msec __read_mostly;
-@@ -836,15 +1218,22 @@ static inline void printk_delay(void)
+@@ -836,15 +905,22 @@ static inline void printk_delay(void)
}
}
@@ -1449,7 +1063,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
boot_delay_msec();
printk_delay();
-@@ -856,7 +1245,7 @@ asmlinkage int vprintk(const char *fmt,
+@@ -856,7 +932,7 @@ asmlinkage int vprintk(const char *fmt,
/*
* Ouch, printk recursed into itself!
*/
@@ -1458,7 +1072,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
/*
* If a crash is occurring during printk() on this CPU,
* then try to get the crash message out but make sure
-@@ -873,97 +1262,92 @@ asmlinkage int vprintk(const char *fmt,
+@@ -873,97 +949,92 @@ asmlinkage int vprintk(const char *fmt,
lockdep_off();
raw_spin_lock(&logbuf_lock);
@@ -1627,7 +1241,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
*/
if (console_trylock_for_printk(this_cpu))
console_unlock();
-@@ -974,12 +1358,73 @@ out_restore_irqs:
+@@ -974,12 +1045,73 @@ out_restore_irqs:
return printed_len;
}
@@ -1703,7 +1317,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
{
}
-@@ -1217,7 +1662,7 @@ int is_console_locked(void)
+@@ -1217,7 +1349,7 @@ int is_console_locked(void)
}
/*
@@ -1712,7 +1326,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
*/
#define PRINTK_BUF_SIZE 512
-@@ -1253,6 +1698,10 @@ void wake_up_klogd(void)
+@@ -1253,6 +1385,10 @@ void wake_up_klogd(void)
this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP);
}
@@ -1723,7 +1337,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
/**
* console_unlock - unlock the console system
*
-@@ -1263,15 +1712,16 @@ void wake_up_klogd(void)
+@@ -1263,15 +1399,16 @@ void wake_up_klogd(void)
* by printk(). If this is the case, console_unlock(); emits
* the output prior to releasing the lock.
*
@@ -1743,7 +1357,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
if (console_suspended) {
up(&console_sem);
-@@ -1281,17 +1731,41 @@ void console_unlock(void)
+@@ -1281,17 +1418,41 @@ void console_unlock(void)
console_may_schedule = 0;
again:
@@ -1793,7 +1407,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
start_critical_timings();
local_irq_restore(flags);
}
-@@ -1312,8 +1786,7 @@ again:
+@@ -1312,8 +1473,7 @@ again:
* flush, no worries.
*/
raw_spin_lock(&logbuf_lock);
@@ -1803,7 +1417,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
if (retry && console_trylock())
-@@ -1549,7 +2022,8 @@ void register_console(struct console *ne
+@@ -1549,7 +1709,8 @@ void register_console(struct console *ne
* for us.
*/
raw_spin_lock_irqsave(&logbuf_lock, flags);
@@ -1813,7 +1427,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
/*
* We're about to replay the log buffer. Only do this to the
-@@ -1758,6 +2232,9 @@ int kmsg_dump_unregister(struct kmsg_dum
+@@ -1758,6 +1919,9 @@ int kmsg_dump_unregister(struct kmsg_dum
}
EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
@@ -1823,7 +1437,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
/**
* kmsg_dump - dump kernel log to kernel message dumpers.
* @reason: the reason (oops, panic etc) for dumping
-@@ -1767,8 +2244,7 @@ EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
+@@ -1767,8 +1931,7 @@ EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
*/
void kmsg_dump(enum kmsg_dump_reason reason)
{
@@ -1833,7 +1447,7 @@ Signed-off-by: Kay Sievers <kay@vrfy.org>
struct kmsg_dumper *dumper;
const char *s1, *s2;
unsigned long l1, l2;
-@@ -1780,24 +2256,27 @@ void kmsg_dump(enum kmsg_dump_reason rea
+@@ -1780,24 +1943,27 @@ void kmsg_dump(enum kmsg_dump_reason rea
/* Theoretically, the log could move on after we do this, but
there's not a lot we can do about that. The new messages
will overwrite the start of what we dump. */
diff --git a/series b/series
index 7627619..1bf71d3 100644
--- a/series
+++ b/series
@@ -1,2 +1,3 @@
-printk.patch
+printk-records.patch
+printk-devkmsg.patch
kern-cont.patch