Skip to content

Commit b031a68

Browse files
jognesspmladek
authored andcommitted
printk: remove logbuf_lock writer-protection of ringbuffer
Since the ringbuffer is lockless, there is no need for it to be protected by @logbuf_lock. Remove @logbuf_lock writer-protection of the ringbuffer. The reader-protection is not removed because some variables, used by readers, are using @logbuf_lock for synchronization: @syslog_seq, @syslog_time, @syslog_partial, @console_seq, struct kmsg_dumper. For PRINTK_NMI_DIRECT_CONTEXT_MASK, @logbuf_lock usage is not removed because it may be used for dumper synchronization. Without @logbuf_lock synchronization of vprintk_store() it is no longer possible to use the single static buffer for temporarily sprint'ing the message. Instead, use vsnprintf() to determine the length and perform the real vscnprintf() using the area reserved from the ringbuffer. This leads to suboptimal packing of the message data, but will result in less wasted storage than multiple per-cpu buffers to support lockless temporary sprint'ing. Signed-off-by: John Ogness <[email protected]> Reviewed-by: Sergey Senozhatsky <[email protected]> Reviewed-by: Petr Mladek <[email protected]> Signed-off-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 6b91670 commit b031a68

File tree

1 file changed

+98
-40
lines changed

1 file changed

+98
-40
lines changed

kernel/printk/printk.c

Lines changed: 98 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,7 @@ void __init setup_log_buf(int early)
11261126
new_descs, ilog2(new_descs_count),
11271127
new_infos);
11281128

1129-
logbuf_lock_irqsave(flags);
1129+
printk_safe_enter_irqsave(flags);
11301130

11311131
log_buf_len = new_log_buf_len;
11321132
log_buf = new_log_buf;
@@ -1143,7 +1143,7 @@ void __init setup_log_buf(int early)
11431143
*/
11441144
prb = &printk_rb_dynamic;
11451145

1146-
logbuf_unlock_irqrestore(flags);
1146+
printk_safe_exit_irqrestore(flags);
11471147

11481148
if (seq != prb_next_seq(&printk_rb_static)) {
11491149
pr_err("dropped %llu messages\n",
@@ -1861,18 +1861,90 @@ static inline u32 printk_caller_id(void)
18611861
0x80000000 + raw_smp_processor_id();
18621862
}
18631863

1864-
/* Must be called under logbuf_lock. */
1864+
/**
1865+
* parse_prefix - Parse level and control flags.
1866+
*
1867+
* @text: The terminated text message.
1868+
* @level: A pointer to the current level value, will be updated.
1869+
* @lflags: A pointer to the current log flags, will be updated.
1870+
*
1871+
* @level may be NULL if the caller is not interested in the parsed value.
1872+
* Otherwise the variable pointed to by @level must be set to
1873+
* LOGLEVEL_DEFAULT in order to be updated with the parsed value.
1874+
*
1875+
* @lflags may be NULL if the caller is not interested in the parsed value.
1876+
* Otherwise the variable pointed to by @lflags will be OR'd with the parsed
1877+
* value.
1878+
*
1879+
* Return: The length of the parsed level and control flags.
1880+
*/
1881+
static u16 parse_prefix(char *text, int *level, enum log_flags *lflags)
1882+
{
1883+
u16 prefix_len = 0;
1884+
int kern_level;
1885+
1886+
while (*text) {
1887+
kern_level = printk_get_level(text);
1888+
if (!kern_level)
1889+
break;
1890+
1891+
switch (kern_level) {
1892+
case '0' ... '7':
1893+
if (level && *level == LOGLEVEL_DEFAULT)
1894+
*level = kern_level - '0';
1895+
break;
1896+
case 'c': /* KERN_CONT */
1897+
if (lflags)
1898+
*lflags |= LOG_CONT;
1899+
}
1900+
1901+
prefix_len += 2;
1902+
text += 2;
1903+
}
1904+
1905+
return prefix_len;
1906+
}
1907+
1908+
static u16 printk_sprint(char *text, u16 size, int facility, enum log_flags *lflags,
1909+
const char *fmt, va_list args)
1910+
{
1911+
u16 text_len;
1912+
1913+
text_len = vscnprintf(text, size, fmt, args);
1914+
1915+
/* Mark and strip a trailing newline. */
1916+
if (text_len && text[text_len - 1] == '\n') {
1917+
text_len--;
1918+
*lflags |= LOG_NEWLINE;
1919+
}
1920+
1921+
/* Strip log level and control flags. */
1922+
if (facility == 0) {
1923+
u16 prefix_len;
1924+
1925+
prefix_len = parse_prefix(text, NULL, NULL);
1926+
if (prefix_len) {
1927+
text_len -= prefix_len;
1928+
memmove(text, text + prefix_len, text_len);
1929+
}
1930+
}
1931+
1932+
return text_len;
1933+
}
1934+
1935+
__printf(4, 0)
18651936
int vprintk_store(int facility, int level,
18661937
const struct dev_printk_info *dev_info,
18671938
const char *fmt, va_list args)
18681939
{
18691940
const u32 caller_id = printk_caller_id();
1870-
static char textbuf[LOG_LINE_MAX];
18711941
struct prb_reserved_entry e;
18721942
enum log_flags lflags = 0;
18731943
struct printk_record r;
18741944
u16 trunc_msg_len = 0;
1875-
char *text = textbuf;
1945+
char prefix_buf[8];
1946+
u16 reserve_size;
1947+
va_list args2;
18761948
u16 text_len;
18771949
u64 ts_nsec;
18781950

@@ -1885,35 +1957,21 @@ int vprintk_store(int facility, int level,
18851957
ts_nsec = local_clock();
18861958

18871959
/*
1888-
* The printf needs to come first; we need the syslog
1889-
* prefix which might be passed-in as a parameter.
1960+
* The sprintf needs to come first since the syslog prefix might be
1961+
* passed in as a parameter. An extra byte must be reserved so that
1962+
* later the vscnprintf() into the reserved buffer has room for the
1963+
* terminating '\0', which is not counted by vsnprintf().
18901964
*/
1891-
text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
1892-
1893-
/* mark and strip a trailing newline */
1894-
if (text_len && text[text_len-1] == '\n') {
1895-
text_len--;
1896-
lflags |= LOG_NEWLINE;
1897-
}
1898-
1899-
/* strip kernel syslog prefix and extract log level or control flags */
1900-
if (facility == 0) {
1901-
int kern_level;
1965+
va_copy(args2, args);
1966+
reserve_size = vsnprintf(&prefix_buf[0], sizeof(prefix_buf), fmt, args2) + 1;
1967+
va_end(args2);
19021968

1903-
while ((kern_level = printk_get_level(text)) != 0) {
1904-
switch (kern_level) {
1905-
case '0' ... '7':
1906-
if (level == LOGLEVEL_DEFAULT)
1907-
level = kern_level - '0';
1908-
break;
1909-
case 'c': /* KERN_CONT */
1910-
lflags |= LOG_CONT;
1911-
}
1969+
if (reserve_size > LOG_LINE_MAX)
1970+
reserve_size = LOG_LINE_MAX;
19121971

1913-
text_len -= 2;
1914-
text += 2;
1915-
}
1916-
}
1972+
/* Extract log level or control flags. */
1973+
if (facility == 0)
1974+
parse_prefix(&prefix_buf[0], &level, &lflags);
19171975

19181976
if (level == LOGLEVEL_DEFAULT)
19191977
level = default_message_loglevel;
@@ -1922,9 +1980,10 @@ int vprintk_store(int facility, int level,
19221980
lflags |= LOG_NEWLINE;
19231981

19241982
if (lflags & LOG_CONT) {
1925-
prb_rec_init_wr(&r, text_len);
1983+
prb_rec_init_wr(&r, reserve_size);
19261984
if (prb_reserve_in_last(&e, prb, &r, caller_id, LOG_LINE_MAX)) {
1927-
memcpy(&r.text_buf[r.info->text_len], text, text_len);
1985+
text_len = printk_sprint(&r.text_buf[r.info->text_len], reserve_size,
1986+
facility, &lflags, fmt, args);
19281987
r.info->text_len += text_len;
19291988

19301989
if (lflags & LOG_NEWLINE) {
@@ -1943,18 +2002,18 @@ int vprintk_store(int facility, int level,
19432002
* prb_reserve_in_last() and prb_reserve() purposely invalidate the
19442003
* structure when they fail.
19452004
*/
1946-
prb_rec_init_wr(&r, text_len);
2005+
prb_rec_init_wr(&r, reserve_size);
19472006
if (!prb_reserve(&e, prb, &r)) {
19482007
/* truncate the message if it is too long for empty buffer */
1949-
truncate_msg(&text_len, &trunc_msg_len);
2008+
truncate_msg(&reserve_size, &trunc_msg_len);
19502009

1951-
prb_rec_init_wr(&r, text_len + trunc_msg_len);
2010+
prb_rec_init_wr(&r, reserve_size + trunc_msg_len);
19522011
if (!prb_reserve(&e, prb, &r))
19532012
return 0;
19542013
}
19552014

19562015
/* fill message */
1957-
memcpy(&r.text_buf[0], text, text_len);
2016+
text_len = printk_sprint(&r.text_buf[0], reserve_size, facility, &lflags, fmt, args);
19582017
if (trunc_msg_len)
19592018
memcpy(&r.text_buf[text_len], trunc_msg, trunc_msg_len);
19602019
r.info->text_len = text_len + trunc_msg_len;
@@ -1995,10 +2054,9 @@ asmlinkage int vprintk_emit(int facility, int level,
19952054
boot_delay_msec(level);
19962055
printk_delay();
19972056

1998-
/* This stops the holder of console_sem just where we want him */
1999-
logbuf_lock_irqsave(flags);
2057+
printk_safe_enter_irqsave(flags);
20002058
printed_len = vprintk_store(facility, level, dev_info, fmt, args);
2001-
logbuf_unlock_irqrestore(flags);
2059+
printk_safe_exit_irqrestore(flags);
20022060

20032061
/* If called from the scheduler, we can not call up(). */
20042062
if (!in_sched) {

0 commit comments

Comments
 (0)