Commit 7ff9554b authored by Kay Sievers's avatar Kay Sievers Committed by Greg Kroah-Hartman

printk: convert byte-buffer to variable-length record buffer

- Record-based stream instead of the traditional byte stream
  buffer. All records carry a 64 bit timestamp, the syslog facility
  and priority in the record header.

- Records consume almost the same amount, sometimes less memory than
  the traditional byte stream buffer (if printk_time is enabled). The record
  header is 16 bytes long, plus some padding bytes at the end if needed.
  The byte-stream buffer needed 3 chars for the syslog prefix, 15 char for
  the timestamp and a newline.

- Buffer management is based on message sequence numbers. When records
  need to be discarded, the reading heads move on to the next full
  record. Unlike the byte-stream buffer, no old logged lines get
  truncated or partly overwritten by new ones. Sequence numbers also
  allow consumers of the log stream to get notified if any message in
  the stream they are about to read gets discarded during the time
  of reading.

- Better buffered IO support for KERN_CONT continuation lines, when printk()
  is called multiple times for a single line. The use of KERN_CONT is now
  mandatory to use continuation; a few places in the kernel need trivial fixes
  here. The buffering could possibly be extended to per-cpu variables to allow
  better thread-safety for multiple printk() invocations for a single line.

- Full-featured syslog facility value support. Different facilities
  can tag their messages. All userspace-injected messages enforce a
  facility value > 0 now, to be able to reliably distinguish them from
  the kernel-generated messages. Independent subsystems like a
  baseband processor running its own firmware, or a kernel-related
  userspace process can use their own unique facility values. Multiple
  independent log streams can co-exist that way in the same
  buffer. All share the same global sequence number counter to ensure
  proper ordering (and interleaving) and to allow the consumers of the
  log to reliably correlate the events from different facilities.
Tested-by: default avatarWilliam Douglas <william.douglas@intel.com>
Signed-off-by: default avatarKay Sievers <kay@vrfy.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 89528127
...@@ -810,33 +810,54 @@ static const struct file_operations oldmem_fops = { ...@@ -810,33 +810,54 @@ static const struct file_operations oldmem_fops = {
static ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv, static ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv,
unsigned long count, loff_t pos) unsigned long count, loff_t pos)
{ {
char *line, *p; char *buf, *line;
int i; int i;
ssize_t ret = -EFAULT; int level = default_message_loglevel;
int facility = 1; /* LOG_USER */
size_t len = iov_length(iv, count); size_t len = iov_length(iv, count);
ssize_t ret = len;
line = kmalloc(len + 1, GFP_KERNEL); if (len > 1024)
if (line == NULL) return -EINVAL;
buf = kmalloc(len+1, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM; return -ENOMEM;
/* line = buf;
* 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++) { for (i = 0; i < count; i++) {
if (copy_from_user(p, iv[i].iov_base, iv[i].iov_len)) if (copy_from_user(line, iv[i].iov_base, iv[i].iov_len))
goto out; 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_emit(facility, level, NULL, 0, "%s", line);
/* printk can add a prefix */
if (ret > len)
ret = len;
out: out:
kfree(line); kfree(buf);
return ret; return ret;
} }
......
...@@ -95,8 +95,19 @@ extern int printk_needs_cpu(int cpu); ...@@ -95,8 +95,19 @@ extern int printk_needs_cpu(int cpu);
extern void printk_tick(void); extern void printk_tick(void);
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
asmlinkage __printf(5, 0)
int vprintk_emit(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args);
asmlinkage __printf(1, 0) asmlinkage __printf(1, 0)
int vprintk(const char *fmt, va_list args); int vprintk(const char *fmt, va_list args);
asmlinkage __printf(5, 6) __cold
asmlinkage int printk_emit(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, ...);
asmlinkage __printf(1, 2) __cold asmlinkage __printf(1, 2) __cold
int printk(const char *fmt, ...); int printk(const char *fmt, ...);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment