linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] printk: More consistent loglevel for continuous lines
@ 2021-11-24 15:48 Petr Mladek
  2021-11-25  0:53 ` Sergey Senozhatsky
  0 siblings, 1 reply; 5+ messages in thread
From: Petr Mladek @ 2021-11-24 15:48 UTC (permalink / raw)
  To: John Ogness, Sergey Senozhatsky, Steven Rostedt
  Cc: Linus Torvalds, Joe Perches, linux-kernel, Petr Mladek

pr_cont() tries to append the message to the previous one. It works
when the last message in the logbuffer was added by the same process
and IRQ context. Otherwise, it is stored as a new message with
the default message loglevel.

CONFIG_MESSAGE_LOGLEVEL_DEFAULT is "4" by default. It means that
the non-appendable pieces are printed with KERN_WARNING.

Many people monitor only more serial messages, including warnings.
They are confused when they see partial messages that do not make
any sense without the context. They even do not have any chance
to see the context when it is filtered by console_loglevel.

The commit 4bcc595ccd80decb424509 ("printk: reinstate KERN_CONT for
printing continuation lines") added possibility to define a particular
loglevel together with KERN_CONT, for example:

   pr_cont(KERN_INFO "more bla bla");

It has been added in v4.9-rc1, 5 years ago. But it seems that nobody
has used it since then. It is possible that people do not know about
it or nobody cared enough.

Anyway, it looks a bit non-practical to update all existing pr_cont()
callers:

	$> git grep "pr_cont" | wc -l
	2054

There seems to be much easier solution. We could remember the last
used loglevel per-context and use it for the continuous lines.

Note that it does not help when another message is printed between
the pieces in the same context, for example, a nested warning.
In this case, the explicit log level would work better. But the saved
loglevel should be good enough in most cases because pr_cont() is often
used in a relatively simple self-contained code. Also the same
caller is easy to detect when CONFIG_PRINTK_CALLER is enabled.

Is it worth it?

On one hand, printk() is the best effort approach. Messages are lost
when the logbuffer is overwritten before they are read by userspace
log daemons and shown on consoles. The continuous lines are even less
reliable. Only the last messages can be appended. Any temporary buffering
might cause losing the messages when the system crashes.

On the other hand, we have already accepted a lot of complexity by
introducing the record based logbuffer format. It was because
people wanted to have as consistent messages as possible.

pr_cont() is heavily used and removes ugly twists in the callers' code.
Some code complexity is acceptable on the printk() side.

The proposed solution is self-contained and quite trivial. It looks
worth trying.

Signed-off-by: Petr Mladek <pmladek@suse.com>
---
 include/linux/sched.h  |  4 +++
 kernel/printk/printk.c | 74 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/include/linux/sched.h b/include/linux/sched.h
index 78c351e35fec..4a23f3341d29 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1140,6 +1140,10 @@ struct task_struct {
 	int				softirq_disable_cnt;
 #endif
 
+#ifdef CONFIG_PRINTK
+	u8				printk_loglevel;
+#endif
+
 #ifdef CONFIG_LOCKDEP
 # define MAX_LOCK_DEPTH			48UL
 	u64				curr_chain_key;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 57b132b658e1..b644dee8f897 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2021,6 +2021,62 @@ static inline u32 printk_caller_id(void)
 		0x80000000 + raw_smp_processor_id();
 }
 
+/* Support to use the loglevel of the last message for continuous lines. */
+#ifdef CONFIG_HAVE_NMI
+#define PRINTK_IRQ_CTX_NUM	3
+#else
+#define PRINTK_IRQ_CTX_NUM	2
+#endif
+
+static DEFINE_PER_CPU(u8, printk_loglevel_irq_ctx[PRINTK_IRQ_CTX_NUM]);
+static u8 printk_loglevel_irq_ctx_early[PRINTK_IRQ_CTX_NUM];
+
+/* Return pointer where the loglevel is stored for the current context. */
+static u8 *printk_loglevel_ctx_var(void)
+{
+	unsigned char irq_ctx_level = interrupt_context_level();
+
+	/* normal process context */
+	if (irq_ctx_level == 0)
+		return &current->printk_loglevel;
+
+	/* IRQ context */
+	if (WARN_ON_ONCE(irq_ctx_level > PRINTK_IRQ_CTX_NUM))
+		return NULL;
+
+	if (printk_percpu_data_ready())
+		return this_cpu_ptr(&printk_loglevel_irq_ctx[irq_ctx_level - 1]);
+	else
+		return &printk_loglevel_irq_ctx_early[irq_ctx_level - 1];
+}
+
+static void printk_write_loglevel_ctx(int loglevel)
+{
+	u8 *loglevel_var = printk_loglevel_ctx_var();
+
+	if (!loglevel_var)
+		return;
+
+	/*
+	 * Remember only the really used loglevels that can be stored
+	 * within 3 bytes in struct printk_info.
+	 */
+	if (WARN_ON_ONCE(loglevel != LOG_LEVEL(loglevel)))
+		return;
+
+	*loglevel_var = loglevel;
+}
+
+static u8 printk_read_loglevel_ctx(void)
+{
+	u8 *loglevel_var = printk_loglevel_ctx_var();
+
+	if (!loglevel_var)
+		return LOGLEVEL_DEFAULT;
+
+	return *loglevel_var;
+}
+
 /**
  * printk_parse_prefix - Parse level and control flags.
  *
@@ -2066,6 +2122,21 @@ u16 printk_parse_prefix(const char *text, int *level,
 	return prefix_len;
 }
 
+static int printk_sanitize_loglevel(int loglevel, enum printk_info_flags flags)
+{
+	/* For continuous lines, fallback to the previously used loglevel. */
+	if (flags & LOG_CONT && loglevel == LOGLEVEL_DEFAULT)
+		loglevel = printk_read_loglevel_ctx();
+
+	if (loglevel == LOGLEVEL_DEFAULT)
+		loglevel = default_message_loglevel;
+
+	/* Remember the really used loglevel for this context. */
+	printk_write_loglevel_ctx(loglevel);
+
+	return loglevel;
+}
+
 __printf(5, 0)
 static u16 printk_sprint(char *text, u16 size, int facility,
 			 enum printk_info_flags *flags, const char *fmt,
@@ -2142,8 +2213,7 @@ int vprintk_store(int facility, int level,
 	if (facility == 0)
 		printk_parse_prefix(&prefix_buf[0], &level, &flags);
 
-	if (level == LOGLEVEL_DEFAULT)
-		level = default_message_loglevel;
+	level = printk_sanitize_loglevel(level, flags);
 
 	if (dev_info)
 		flags |= LOG_NEWLINE;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [RFC] printk: More consistent loglevel for continuous lines
  2021-11-24 15:48 [RFC] printk: More consistent loglevel for continuous lines Petr Mladek
@ 2021-11-25  0:53 ` Sergey Senozhatsky
  2021-11-25  1:45   ` Joe Perches
  2021-12-06 17:34   ` Petr Mladek
  0 siblings, 2 replies; 5+ messages in thread
From: Sergey Senozhatsky @ 2021-11-25  0:53 UTC (permalink / raw)
  To: Petr Mladek
  Cc: John Ogness, Sergey Senozhatsky, Steven Rostedt, Linus Torvalds,
	Joe Perches, linux-kernel

On (21/11/24 16:48), Petr Mladek wrote:
> 
> Anyway, it looks a bit non-practical to update all existing pr_cont()
> callers:
> 
> 	$> git grep "pr_cont" | wc -l
> 	2054

Another question is how many pr_cont()-s are getting compiled with
the "average" production kernel config. A number of pr_cont() is in
debugging code - lockdep, kasan, etc. - which is not compiled for prod.

[..]

> +static void printk_write_loglevel_ctx(int loglevel)
> +{
> +	u8 *loglevel_var = printk_loglevel_ctx_var();
> +
> +	if (!loglevel_var)
> +		return;
> +
> +	/*
> +	 * Remember only the really used loglevels that can be stored
> +	 * within 3 bytes in struct printk_info.
> +	 */
> +	if (WARN_ON_ONCE(loglevel != LOG_LEVEL(loglevel)))
> +		return;
> +
> +	*loglevel_var = loglevel;
> +}
> +
> +static u8 printk_read_loglevel_ctx(void)
> +{
> +	u8 *loglevel_var = printk_loglevel_ctx_var();
> +
> +	if (!loglevel_var)
> +		return LOGLEVEL_DEFAULT;
> +
> +	return *loglevel_var;
> +}
> +
>  /**
>   * printk_parse_prefix - Parse level and control flags.
>   *
> @@ -2066,6 +2122,21 @@ u16 printk_parse_prefix(const char *text, int *level,
>  	return prefix_len;
>  }
>  
> +static int printk_sanitize_loglevel(int loglevel, enum printk_info_flags flags)
> +{
> +	/* For continuous lines, fallback to the previously used loglevel. */
> +	if (flags & LOG_CONT && loglevel == LOGLEVEL_DEFAULT)
> +		loglevel = printk_read_loglevel_ctx();
> +
> +	if (loglevel == LOGLEVEL_DEFAULT)
> +		loglevel = default_message_loglevel;
> +
> +	/* Remember the really used loglevel for this context. */
> +	printk_write_loglevel_ctx(loglevel);
> +
> +	return loglevel;
> +}

Let's assume something like this

	foo()
	{
		char *s;

		pr_cont("Blah ");
		s = arch_foo();
		pr_cont("%s \n", s);
	}

Suppose that arch_foo() errs and pr_warn()-s. Are we going to use WARN
level for trailing pr_cont()?

	pr_cont("Blah ") -> printk_write_loglevel_ctx(default)
	pr_warn() -> printk_write_loglevel_ctx(warn)
	pr_cont("%s \n"") <- printk_read_loglevel_ctx(warn)



p.s. I'm still reading amazingly detailed commit messages from your console
registration patch set.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] printk: More consistent loglevel for continuous lines
  2021-11-25  0:53 ` Sergey Senozhatsky
@ 2021-11-25  1:45   ` Joe Perches
  2021-12-06 16:19     ` Petr Mladek
  2021-12-06 17:34   ` Petr Mladek
  1 sibling, 1 reply; 5+ messages in thread
From: Joe Perches @ 2021-11-25  1:45 UTC (permalink / raw)
  To: Sergey Senozhatsky, Petr Mladek
  Cc: John Ogness, Steven Rostedt, Linus Torvalds, linux-kernel

On Thu, 2021-11-25 at 09:53 +0900, Sergey Senozhatsky wrote:
> On (21/11/24 16:48), Petr Mladek wrote:
> > 
> > Anyway, it looks a bit non-practical to update all existing pr_cont()
> > callers:
> > 
> > 	$> git grep "pr_cont" | wc -l
> > 	2054
> 
> Another question is how many pr_cont()-s are getting compiled with
> the "average" production kernel config. A number of pr_cont() is in
> debugging code - lockdep, kasan, etc. - which is not compiled for prod.

This was Linus' attempt to make KERN_CONT meaningful again when it
was IMO a slightly different issue to fix bare printks.

Linus changed the few bare printks that mattered to him at the time.

So the existing bare printk's should also be inspected.

There are still many hundreds of those bare printks around.

$ git grep -P '\bprintk\s*\(\s*(?!KERN_[A-Z]+)\s*"' | wc -l
6863

But most all of those are in really old and effectively dead code.

To approximately answer to the question of how many pr_cont's and
bare printks are getting compiled in an x86-64 config:

$ make defconfig
$ make clean
$ make
$ find . -name '*.o' | sed 's/\.o$/\.c/' | \
  xargs git grep -P '(\bprintk\s*\(\s*(?!KERN_[A-Z]+)|\bpr_cont\b|\bprintk\s*\(\s*KERN_CONT)' -- | \
  wc -l
518




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] printk: More consistent loglevel for continuous lines
  2021-11-25  1:45   ` Joe Perches
@ 2021-12-06 16:19     ` Petr Mladek
  0 siblings, 0 replies; 5+ messages in thread
From: Petr Mladek @ 2021-12-06 16:19 UTC (permalink / raw)
  To: Joe Perches
  Cc: Sergey Senozhatsky, John Ogness, Steven Rostedt, Linus Torvalds,
	linux-kernel

On Wed 2021-11-24 17:45:57, Joe Perches wrote:
> On Thu, 2021-11-25 at 09:53 +0900, Sergey Senozhatsky wrote:
> > On (21/11/24 16:48), Petr Mladek wrote:
> > > 
> > > Anyway, it looks a bit non-practical to update all existing pr_cont()
> > > callers:
> > > 
> > > 	$> git grep "pr_cont" | wc -l
> > > 	2054
> > 
> > Another question is how many pr_cont()-s are getting compiled with
> > the "average" production kernel config. A number of pr_cont() is in
> > debugging code - lockdep, kasan, etc. - which is not compiled for prod.
> 
> This was Linus' attempt to make KERN_CONT meaningful again when it
> was IMO a slightly different issue to fix bare printks.

Yes, the primary motivation for Linus' commit 4bcc595ccd80decb424
("printk: reinstate KERN_CONT for printing continuation lines) was
different. It solved the problem when two unrelated lines were
connected because of a missing newline.

The support for KERN_CONT together with real log level was just
a side effect because some code used it in the meantime.


> Linus changed the few bare printks that mattered to him at the time.

IMHO, Linus was more interested into the missing newlines. He did the
change in v4.9-rc1 and I see a lot of newlines fixes around.

Anyway, I checked v4.9 final sources and found only one location
that combined KERN_CONT with another real log level. It was

     pr_warn(KERN_CONT " trap #%lu: %s%c", code,

in arch/parisc/mm/fault.c. And it was later removed by the commit
commit 8351badf349b22b47c969 ("parisc: fix a printk").

I really can't find any occurrence of KERN_CONT together with
a real log level in the current sources.


We could make it required for new code by checkpatch.pl. But it would
still leave several hunderds/thousands of existing callers to be
fixed. And it seems that people do not care much about it.


Best Regards,
Petr

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] printk: More consistent loglevel for continuous lines
  2021-11-25  0:53 ` Sergey Senozhatsky
  2021-11-25  1:45   ` Joe Perches
@ 2021-12-06 17:34   ` Petr Mladek
  1 sibling, 0 replies; 5+ messages in thread
From: Petr Mladek @ 2021-12-06 17:34 UTC (permalink / raw)
  To: Sergey Senozhatsky
  Cc: John Ogness, Steven Rostedt, Linus Torvalds, Joe Perches, linux-kernel

On Thu 2021-11-25 09:53:49, Sergey Senozhatsky wrote:
> On (21/11/24 16:48), Petr Mladek wrote:
> > 
> > Anyway, it looks a bit non-practical to update all existing pr_cont()
> > callers:
> > 
> > 	$> git grep "pr_cont" | wc -l
> > 	2054
> 
> Another question is how many pr_cont()-s are getting compiled with
> the "average" production kernel config. A number of pr_cont() is in
> debugging code - lockdep, kasan, etc. - which is not compiled for prod.

It is still >500 for x86_64 default configuration as mentioned in
the reply by Joe.


> Let's assume something like this
> 
> 	foo()
> 	{
> 		char *s;
> 
> 		pr_cont("Blah ");
> 		s = arch_foo();
> 		pr_cont("%s \n", s);
> 	}
> 
> Suppose that arch_foo() errs and pr_warn()-s. Are we going to use WARN
> level for trailing pr_cont()?
> 
> 	pr_cont("Blah ") -> printk_write_loglevel_ctx(default)
> 	pr_warn() -> printk_write_loglevel_ctx(warn)
> 	pr_cont("%s \n"") <- printk_read_loglevel_ctx(warn)


Yes, the proposed solution will not work when there is a nested
message in the same context. It was even mentioned in the commit
message.

But nested messages break the continuous lines completely. They
primary break the text.

pr_cont()/KERN_CONT approach could work reasonably only for
self-contained code. Though even these lines might still get
interleaved by messages from another context/CPU. But it has
a solution. caller_id allows to connect the right pieces.
The proposed patch allows to preserve the loglevel and
actually see all the pieces.

pr_cont()/KERN_CONT is bad API for situations where the pieces
are printed by complicated and/or external code. Such code
should use its own buffer or avoid continuous lines at all.


By other words. The patch solves one fundamental problem when
pr_cont() is used reasonably. It does not help in situations
where pr_cont() should not be used at all.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2021-12-06 17:34 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-24 15:48 [RFC] printk: More consistent loglevel for continuous lines Petr Mladek
2021-11-25  0:53 ` Sergey Senozhatsky
2021-11-25  1:45   ` Joe Perches
2021-12-06 16:19     ` Petr Mladek
2021-12-06 17:34   ` Petr Mladek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).