diff options
author | Kay Sievers <kay@vrfy.org> | 2012-04-06 20:12:30 +0200 |
---|---|---|
committer | Kay Sievers <kay@vrfy.org> | 2012-04-06 20:12:30 +0200 |
commit | f260f8f17e9dcd610b119c725846c838943e197a (patch) | |
tree | 3a1599225c57dbbc55d2ea40cdff59e606265f5c | |
parent | 86c1cbd04cf88fae08303c55baf364db9698d8bf (diff) | |
download | patches-f260f8f17e9dcd610b119c725846c838943e197a.tar.gz |
split out record-buffer patch
-rw-r--r-- | printk-devkmsg.patch | 505 | ||||
-rw-r--r-- | printk-records.patch (renamed from printk.patch) | 564 | ||||
-rw-r--r-- | series | 3 |
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. */ @@ -1,2 +1,3 @@ -printk.patch +printk-records.patch +printk-devkmsg.patch kern-cont.patch |