Skip to content

Commit 7ff9554

Browse files
kaysieversgregkh
authored andcommitted
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: William Douglas <[email protected]> Signed-off-by: Kay Sievers <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 8952812 commit 7ff9554

File tree

3 files changed

+639
-441
lines changed

3 files changed

+639
-441
lines changed

drivers/char/mem.c

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -810,33 +810,54 @@ static const struct file_operations oldmem_fops = {
810810
static ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv,
811811
unsigned long count, loff_t pos)
812812
{
813-
char *line, *p;
813+
char *buf, *line;
814814
int i;
815-
ssize_t ret = -EFAULT;
815+
int level = default_message_loglevel;
816+
int facility = 1; /* LOG_USER */
816817
size_t len = iov_length(iv, count);
818+
ssize_t ret = len;
817819

818-
line = kmalloc(len + 1, GFP_KERNEL);
819-
if (line == NULL)
820+
if (len > 1024)
821+
return -EINVAL;
822+
buf = kmalloc(len+1, GFP_KERNEL);
823+
if (buf == NULL)
820824
return -ENOMEM;
821825

822-
/*
823-
* copy all vectors into a single string, to ensure we do
824-
* not interleave our log line with other printk calls
825-
*/
826-
p = line;
826+
line = buf;
827827
for (i = 0; i < count; i++) {
828-
if (copy_from_user(p, iv[i].iov_base, iv[i].iov_len))
828+
if (copy_from_user(line, iv[i].iov_base, iv[i].iov_len))
829829
goto out;
830-
p += iv[i].iov_len;
830+
line += iv[i].iov_len;
831+
}
832+
833+
/*
834+
* Extract and skip the syslog prefix <[0-9]*>. Coming from userspace
835+
* the decimal value represents 32bit, the lower 3 bit are the log
836+
* level, the rest are the log facility.
837+
*
838+
* If no prefix or no userspace facility is specified, we
839+
* enforce LOG_USER, to be able to reliably distinguish
840+
* kernel-generated messages from userspace-injected ones.
841+
*/
842+
line = buf;
843+
if (line[0] == '<') {
844+
char *endp = NULL;
845+
846+
i = simple_strtoul(line+1, &endp, 10);
847+
if (endp && endp[0] == '>') {
848+
level = i & 7;
849+
if (i >> 3)
850+
facility = i >> 3;
851+
endp++;
852+
len -= endp - line;
853+
line = endp;
854+
}
831855
}
832-
p[0] = '\0';
856+
line[len] = '\0';
833857

834-
ret = printk("%s", line);
835-
/* printk can add a prefix */
836-
if (ret > len)
837-
ret = len;
858+
printk_emit(facility, level, NULL, 0, "%s", line);
838859
out:
839-
kfree(line);
860+
kfree(buf);
840861
return ret;
841862
}
842863

include/linux/printk.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,19 @@ extern int printk_needs_cpu(int cpu);
9595
extern void printk_tick(void);
9696

9797
#ifdef CONFIG_PRINTK
98+
asmlinkage __printf(5, 0)
99+
int vprintk_emit(int facility, int level,
100+
const char *dict, size_t dictlen,
101+
const char *fmt, va_list args);
102+
98103
asmlinkage __printf(1, 0)
99104
int vprintk(const char *fmt, va_list args);
105+
106+
asmlinkage __printf(5, 6) __cold
107+
asmlinkage int printk_emit(int facility, int level,
108+
const char *dict, size_t dictlen,
109+
const char *fmt, ...);
110+
100111
asmlinkage __printf(1, 2) __cold
101112
int printk(const char *fmt, ...);
102113

0 commit comments

Comments
 (0)