Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1626619 > unrolled thread
| Started by | Steven Rostedt <rostedt@goodmis.org> |
|---|---|
| First post | 2017-04-19 19:20 +0200 |
| Last post | 2017-04-24 04:20 +0200 |
| Articles | 7 — 4 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: [PATCH v5 1/4] printk/nmi: generic solution for safe printk in NMI Steven Rostedt <rostedt@goodmis.org> - 2017-04-19 19:20 +0200
Re: [PATCH v5 1/4] printk/nmi: generic solution for safe printk in NMI Peter Zijlstra <peterz@infradead.org> - 2017-04-19 19:30 +0200
Re: [PATCH v5 1/4] printk/nmi: generic solution for safe printk in NMI Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com> - 2017-04-20 05:40 +0200
Re: [PATCH v5 1/4] printk/nmi: generic solution for safe printk in NMI Petr Mladek <pmladek@suse.com> - 2017-04-20 15:20 +0200
Re: [PATCH v5 1/4] printk/nmi: generic solution for safe printk in NMI Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com> - 2017-04-21 04:00 +0200
Re: [PATCH v5 1/4] printk/nmi: generic solution for safe printk in NMI Petr Mladek <pmladek@suse.com> - 2017-04-21 14:10 +0200
Re: [PATCH v5 1/4] printk/nmi: generic solution for safe printk in NMI Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com> - 2017-04-24 04:20 +0200
| From | Steven Rostedt <rostedt@goodmis.org> |
|---|---|
| Date | 2017-04-19 19:20 +0200 |
| Subject | Re: [PATCH v5 1/4] printk/nmi: generic solution for safe printk in NMI |
| Message-ID | <ty1qO-6bq-33@gated-at.bofh.it> |
On Thu, 21 Apr 2016 13:48:42 +0200
Petr Mladek <pmladek@suse.com> wrote:
> printk() takes some locks and could not be used a safe way in NMI context.
I just found a problem with this solution. It kills ftrace dumps from
NMI context :-(
[ 1295.168495] <...>-67423 10dNh1 382171111us : do_raw_spin_lock <-_raw_spin_lock
[ 1295.168495] <...>-67423 10dNh1 382171111us : sched_stat_runtime: comm=cc1 pid=67423 runtime=96858 [ns] vruntime=11924198270 [ns]
[ 1295.168496] <...>-67423 10dNh1 382171111us : lock_acquire: ffffffff81c5c940 read rcu_read_lock
[ 1295.168497]
[ 1295.168498] Lost 4890096 message(s)!
[ 1296.805063] ---[ end Kernel panic - not syncing: Hard LOCKUP
[ 1296.811553] unchecked MSR access error: WRMSR to 0x83f (tried to write 0x00000000000000f6) at rIP: 0xffffffff81046fc7 (native_apic_msr_write+0x27/0x40)
[ 1296.811553] Call Trace:
[ 1296.811553] <NMI>
I was hoping to see a cause of a hard lockup by enabling
ftrace_dump_on_oops. But as NMIs now have a very small buffer that
gets flushed, we need to find a new way to print out the full ftrace
buffer over serial.
Thoughts?
-- Steve
>
> The chance of a deadlock is real especially when printing stacks from all
> CPUs. This particular problem has been addressed on x86 by the commit
> a9edc8809328 ("x86/nmi: Perform a safe NMI stack trace on all CPUs").
>
> The patchset brings two big advantages. First, it makes the NMI
> backtraces safe on all architectures for free. Second, it makes all NMI
> messages almost safe on all architectures (the temporary buffer is
> limited. We still should keep the number of messages in NMI context at
> minimum).
>
> Note that there already are several messages printed in NMI context:
> WARN_ON(in_nmi()), BUG_ON(in_nmi()), anything being printed out from MCE
> handlers. These are not easy to avoid.
>
> This patch reuses most of the code and makes it generic. It is useful for
> all messages and architectures that support NMI.
>
> The alternative printk_func is set when entering and is reseted when
> leaving NMI context. It queues IRQ work to copy the messages into the
> main ring buffer in a safe context.
>
> __printk_nmi_flush() copies all available messages and reset the buffer.
> Then we could use a simple cmpxchg operations to get synchronized with
> writers. There is also used a spinlock to get synchronized with other
> flushers.
>
> We do not longer use seq_buf because it depends on external lock. It
> would be hard to make all supported operations safe for a lockless use.
> It would be confusing and error prone to make only some operations safe.
>
> The code is put into separate printk/nmi.c as suggested by Steven Rostedt.
> It needs a per-CPU buffer and is compiled only on architectures that call
> nmi_enter(). This is achieved by the new HAVE_NMI Kconfig flag.
>
> The are MN10300 and Xtensa architectures. We need to clean up NMI
> handling there first. Let's do it separately.
>
> The patch is heavily based on the draft from Peter Zijlstra, see
> https://lkml.org/lkml/2015/6/10/327
>
> [arnd@arndb.de: printk-nmi: use %zu format string for size_t]
> [akpm@linux-foundation.org: min_t->min - all types are size_t here]
> Signed-off-by: Petr Mladek <pmladek@suse.com>
> Suggested-by: Peter Zijlstra <peterz@infradead.org>
> Suggested-by: Steven Rostedt <rostedt@goodmis.org>
> Cc: Jan Kara <jack@suse.cz>
> Cc: Russell King <rmk+kernel@arm.linux.org.uk>
> Cc: Daniel Thompson <daniel.thompson@linaro.org>
> Cc: Jiri Kosina <jkosina@suse.com>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ralf Baechle <ralf@linux-mips.org>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
> Cc: David Miller <davem@davemloft.net>
> Cc: Daniel Thompson <daniel.thompson@linaro.org>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
[toc] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2017-04-19 19:30 +0200 |
| Message-ID | <ty1At-6ev-1@gated-at.bofh.it> |
| In reply to | #1626619 |
On Wed, Apr 19, 2017 at 01:13:41PM -0400, Steven Rostedt wrote: > On Thu, 21 Apr 2016 13:48:42 +0200 > Petr Mladek <pmladek@suse.com> wrote: > > > printk() takes some locks and could not be used a safe way in NMI context. > > I just found a problem with this solution. It kills ftrace dumps from > NMI context :-( > > [ 1295.168495] <...>-67423 10dNh1 382171111us : do_raw_spin_lock <-_raw_spin_lock > [ 1295.168495] <...>-67423 10dNh1 382171111us : sched_stat_runtime: comm=cc1 pid=67423 runtime=96858 [ns] vruntime=11924198270 [ns] > [ 1295.168496] <...>-67423 10dNh1 382171111us : lock_acquire: ffffffff81c5c940 read rcu_read_lock > [ 1295.168497] > [ 1295.168498] Lost 4890096 message(s)! > [ 1296.805063] ---[ end Kernel panic - not syncing: Hard LOCKUP > [ 1296.811553] unchecked MSR access error: WRMSR to 0x83f (tried to write 0x00000000000000f6) at rIP: 0xffffffff81046fc7 (native_apic_msr_write+0x27/0x40) > [ 1296.811553] Call Trace: > [ 1296.811553] <NMI> > > I was hoping to see a cause of a hard lockup by enabling > ftrace_dump_on_oops. But as NMIs now have a very small buffer that > gets flushed, we need to find a new way to print out the full ftrace > buffer over serial. > > Thoughts? early_printk ;-)
[toc] | [prev] | [next] | [standalone]
| From | Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com> |
|---|---|
| Date | 2017-04-20 05:40 +0200 |
| Message-ID | <tyb6N-3Js-13@gated-at.bofh.it> |
| In reply to | #1626619 |
Hello Steven,
On (04/19/17 13:13), Steven Rostedt wrote:
> > printk() takes some locks and could not be used a safe way in NMI context.
>
> I just found a problem with this solution. It kills ftrace dumps from
> NMI context :-(
>
> [ 1295.168495] <...>-67423 10dNh1 382171111us : do_raw_spin_lock <-_raw_spin_lock
> [ 1295.168495] <...>-67423 10dNh1 382171111us : sched_stat_runtime: comm=cc1 pid=67423 runtime=96858 [ns] vruntime=11924198270 [ns]
> [ 1295.168496] <...>-67423 10dNh1 382171111us : lock_acquire: ffffffff81c5c940 read rcu_read_lock
> [ 1295.168497]
> [ 1295.168498] Lost 4890096 message(s)!
> [ 1296.805063] ---[ end Kernel panic - not syncing: Hard LOCKUP
> [ 1296.811553] unchecked MSR access error: WRMSR to 0x83f (tried to write 0x00000000000000f6) at rIP: 0xffffffff81046fc7 (native_apic_msr_write+0x27/0x40)
> [ 1296.811553] Call Trace:
> [ 1296.811553] <NMI>
>
> I was hoping to see a cause of a hard lockup by enabling
> ftrace_dump_on_oops. But as NMIs now have a very small buffer that
> gets flushed, we need to find a new way to print out the full ftrace
> buffer over serial.
>
> Thoughts?
hmmm... a really tough one.
well, someone has to say this:
the simplest thing is to have a bigger PRINTK_SAFE_LOG_BUF_SHIFT value :)
just thinking (well, sort of) out loud. the problem is that we can't tell if
we already hold any printk related locks ("printk related locks" is not even
well defined term). so printk from NMI can deadlock or it can be OK, we
never know. but looking and vprintk_emit() and console_unlock() it seems that
we have some sort of a hint now, which is this_cpu_read(printk_context) - if
we are not in printk_safe context then we can say that _probably_ (and that's
a Russian roulette) doing "normal" printk() will work. that is a *very-very*
risky (and admittedly dumb) thing to assume, so we will move in a slightly
different direction. checking this_cpu_read(printk_context) only assures us
that we don't hold `logbuf_lock' on this CPU. and that is sort of something,
at least we can be sure that doing printk_deferred() from this CPU is safe.
printk_deferred() means that your NMI messages will end up in the logbuf,
which is a) bigger in size than per-CPU buffer and b) some other CPU can
immediately print those messages (hopefully).
we also switch to printk_safe mode for call_console_drivers() in
console_unlock(). but we can't make any solid assumptions there - serial
console lock can already be acquired, we don't have any markers for that.
it may be reasonable to assume that if we are not in printk_safe mode on
this CPU then serial console is not locked from this CPU, but there is
nothing that can assure us.
so at the moment what I can think of is something like
-- check this_cpu_read(printk_context) in NMI prink
-- if we are NOT in printk_safe on this CPU, then do printk_deferred()
and bypass `nmi_print_seq' buffer
-- if we are in printk_safe
-- well... bad luck... have a bigger buffer.
.... or there are some other options here, but I'd prefer
not to list them, because people will hate me :)
so this shifts the purpose of `nmi_print_seq' buffer: use it only when
you can't use logbuf. otherwise, do printk_deferred().
need to think more.
-ss
[toc] | [prev] | [next] | [standalone]
| From | Petr Mladek <pmladek@suse.com> |
|---|---|
| Date | 2017-04-20 15:20 +0200 |
| Message-ID | <tyka5-XM-19@gated-at.bofh.it> |
| In reply to | #1626957 |
On Thu 2017-04-20 12:31:12, Sergey Senozhatsky wrote:
> Hello Steven,
>
> On (04/19/17 13:13), Steven Rostedt wrote:
> > > printk() takes some locks and could not be used a safe way in NMI context.
> >
> > I just found a problem with this solution. It kills ftrace dumps from
> > NMI context :-(
> >
> > [ 1295.168495] <...>-67423 10dNh1 382171111us : do_raw_spin_lock <-_raw_spin_lock
> > [ 1295.168495] <...>-67423 10dNh1 382171111us : sched_stat_runtime: comm=cc1 pid=67423 runtime=96858 [ns] vruntime=11924198270 [ns]
> > [ 1295.168496] <...>-67423 10dNh1 382171111us : lock_acquire: ffffffff81c5c940 read rcu_read_lock
> > [ 1295.168497]
> > [ 1295.168498] Lost 4890096 message(s)!
> > [ 1296.805063] ---[ end Kernel panic - not syncing: Hard LOCKUP
> > [ 1296.811553] unchecked MSR access error: WRMSR to 0x83f (tried to write 0x00000000000000f6) at rIP: 0xffffffff81046fc7 (native_apic_msr_write+0x27/0x40)
> > [ 1296.811553] Call Trace:
> > [ 1296.811553] <NMI>
> >
> > I was hoping to see a cause of a hard lockup by enabling
> > ftrace_dump_on_oops. But as NMIs now have a very small buffer that
> > gets flushed, we need to find a new way to print out the full ftrace
> > buffer over serial.
> >
> > Thoughts?
>
> hmmm... a really tough one.
>
> well, someone has to say this:
> the simplest thing is to have a bigger PRINTK_SAFE_LOG_BUF_SHIFT value :)
>
>
> just thinking (well, sort of) out loud. the problem is that we can't tell if
> we already hold any printk related locks ("printk related locks" is not even
> well defined term). so printk from NMI can deadlock or it can be OK, we
> never know. but looking and vprintk_emit() and console_unlock() it seems that
> we have some sort of a hint now, which is this_cpu_read(printk_context) - if
> we are not in printk_safe context then we can say that _probably_ (and that's
> a Russian roulette) doing "normal" printk() will work. that is a *very-very*
> risky (and admittedly dumb) thing to assume, so we will move in a slightly
> different direction. checking this_cpu_read(printk_context) only assures us
> that we don't hold `logbuf_lock' on this CPU. and that is sort of something,
> at least we can be sure that doing printk_deferred() from this CPU is safe.
> printk_deferred() means that your NMI messages will end up in the logbuf,
> which is a) bigger in size than per-CPU buffer and b) some other CPU can
> immediately print those messages (hopefully).
>
> we also switch to printk_safe mode for call_console_drivers() in
> console_unlock(). but we can't make any solid assumptions there - serial
> console lock can already be acquired, we don't have any markers for that.
> it may be reasonable to assume that if we are not in printk_safe mode on
> this CPU then serial console is not locked from this CPU, but there is
> nothing that can assure us.
Good analyze. I would summarize it that we need to be careful of:
+ logbug_lock
+ PRINTK_SAFE_CONTEXT
+ locks used by console drivers
The first two things are easy to check. Except that a check for logbuf_lock
might produce false negatives. The last check is very hard.
> so at the moment what I can think of is something like
>
> -- check this_cpu_read(printk_context) in NMI prink
>
> -- if we are NOT in printk_safe on this CPU, then do printk_deferred()
> and bypass `nmi_print_seq' buffer
I would add also a check for logbuf_lock.
> -- if we are in printk_safe
> -- well... bad luck... have a bigger buffer.
Yup, we do the best effort while still trying to stay on the safe
side.
I have cooked up a patch based on this. It uses printk_deferred()
in NMI when it is safe. Note that console_flush_on_panic() will
try to get them on the console when a kdump is not generated.
I believe that it will help Steven.
From c530d9dee91c74db5e6a198479e2e63b24cb84a2 Mon Sep 17 00:00:00 2001
From: Petr Mladek <pmladek@suse.com>
Date: Thu, 20 Apr 2017 10:52:31 +0200
Subject: [PATCH] printk: Use the main logbuf in NMI when logbuf_lock is
available
The commit 42a0bb3f71383b457a7d ("printk/nmi: generic solution for safe
printk in NMI") caused that printk stores messages into a temporary
buffer in NMI context.
The buffer is per-CPU and therefore the size is rather limited.
It works quite well for NMI backtraces. But there are longer logs
that might get printed in NMI context, for example, lockdep
warnings, ftrace_dump_on_oops.
The temporary buffer is used to avoid deadlocks caused by
logbuf_lock. Also it is needed to avoid races with the other
temporary buffer that is used when PRINTK_SAFE_CONTEXT is entered.
But the main buffer can be used in NMI if the lock is available
and we did not interrupt PRINTK_SAFE_CONTEXT.
The lock is checked using raw_spin_is_locked(). It might cause
false negatives when the lock is taken on another CPU outside NMI.
For this reason, we do the check in printk_nmi_enter(). It makes
the handling consistent for the entire NMI handler and avoids
reshuffling of the messages.
The patch also defines special printk context that allows
to use printk_deferred() in NMI. Note that we could not flush
the messages to the consoles because console drivers might use
many other internal locks.
The newly created vprintk_deferred() disables the preemption
only around the irq work handling. It is needed there to keep
the consistency between the two per-CPU variables. But there
is no reason to disable preemption around vprintk_emit().
Finally, the patch patch puts back explicit serialization
of the NMI backtraces from different CPUs. It was removed
by the commit a9edc88093287183ac934b ("x86/nmi: Perform
a safe NMI stack trace on all CPUs"). It was not needed
because the flushing of the temporary per-CPU buffers
was serialized.
Suggested-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Signed-off-by: Petr Mladek <pmladek@suse.com>
---
kernel/printk/internal.h | 6 ++++--
kernel/printk/printk.c | 19 ++++++++++++++-----
kernel/printk/printk_safe.c | 25 +++++++++++++++++++++++--
lib/nmi_backtrace.c | 3 +++
4 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 1db044f808b7..2a7d04049af4 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -18,12 +18,14 @@
#ifdef CONFIG_PRINTK
-#define PRINTK_SAFE_CONTEXT_MASK 0x7fffffff
-#define PRINTK_NMI_CONTEXT_MASK 0x80000000
+#define PRINTK_SAFE_CONTEXT_MASK 0x3fffffff
+#define PRINTK_NMI_DEFERRED_CONTEXT_MASK 0x40000000
+#define PRINTK_NMI_CONTEXT_MASK 0x80000000
extern raw_spinlock_t logbuf_lock;
__printf(1, 0) int vprintk_default(const char *fmt, va_list args);
+__printf(1, 0) int vprintk_deferred(const char *fmt, va_list args);
__printf(1, 0) int vprintk_func(const char *fmt, va_list args);
void __printk_safe_enter(void);
void __printk_safe_exit(void);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 2984fb0f0257..16b519927d35 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2691,16 +2691,13 @@ void wake_up_klogd(void)
preempt_enable();
}
-int printk_deferred(const char *fmt, ...)
+int vprintk_deferred(const char *fmt, va_list args)
{
- va_list args;
int r;
- preempt_disable();
- va_start(args, fmt);
r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
- va_end(args);
+ preempt_disable();
__this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
preempt_enable();
@@ -2708,6 +2705,18 @@ int printk_deferred(const char *fmt, ...)
return r;
}
+int printk_deferred(const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+ r = vprintk_deferred(fmt, args);
+ va_end(args);
+
+ return r;
+}
+
/*
* printk rate limiting, lifted from the networking subsystem.
*
diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c
index 033e50a7d706..c3d165bcde42 100644
--- a/kernel/printk/printk_safe.c
+++ b/kernel/printk/printk_safe.c
@@ -308,12 +308,23 @@ static int vprintk_nmi(const char *fmt, va_list args)
void printk_nmi_enter(void)
{
- this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
+ /*
+ * The size of the extra per-CPU buffer is limited. Use it
+ * only when really needed.
+ */
+ if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK ||
+ raw_spin_is_locked(&logbuf_lock)) {
+ this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
+ } else {
+ this_cpu_or(printk_context, PRINTK_NMI_DEFERRED_CONTEXT_MASK);
+ }
}
void printk_nmi_exit(void)
{
- this_cpu_and(printk_context, ~PRINTK_NMI_CONTEXT_MASK);
+ this_cpu_and(printk_context,
+ ~(PRINTK_NMI_CONTEXT_MASK ||
+ PRINTK_NMI_DEFERRED_CONTEXT_MASK));
}
#else
@@ -351,12 +362,22 @@ void __printk_safe_exit(void)
__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
{
+ /* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */
if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
return vprintk_nmi(fmt, args);
+ /* Use extra buffer to prevent a recursion deadlock in safe mode. */
if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
return vprintk_safe(fmt, args);
+ /*
+ * Use the main logbuf when logbuf_lock is available in NMI.
+ * But avoid calling console drivers that might have their own locks.
+ */
+ if (this_cpu_read(printk_context) & PRINTK_NMI_DEFERRED_CONTEXT_MASK)
+ return vprintk_deferred(fmt, args);
+
+ /* No obstacles. */
return vprintk_default(fmt, args);
}
diff --git a/lib/nmi_backtrace.c b/lib/nmi_backtrace.c
index 4e8a30d1c22f..0bc0a3535a8a 100644
--- a/lib/nmi_backtrace.c
+++ b/lib/nmi_backtrace.c
@@ -86,9 +86,11 @@ void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
bool nmi_cpu_backtrace(struct pt_regs *regs)
{
+ static arch_spinlock_t lock = __ARCH_SPIN_LOCK_UNLOCKED;
int cpu = smp_processor_id();
if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) {
+ arch_spin_lock(&lock);
if (regs && cpu_in_idle(instruction_pointer(regs))) {
pr_warn("NMI backtrace for cpu %d skipped: idling at pc %#lx\n",
cpu, instruction_pointer(regs));
@@ -99,6 +101,7 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
else
dump_stack();
}
+ arch_spin_unlock(&lock);
cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
return true;
}
--
1.8.5.6
[toc] | [prev] | [next] | [standalone]
| From | Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com> |
|---|---|
| Date | 2017-04-21 04:00 +0200 |
| Message-ID | <tyw1z-84u-3@gated-at.bofh.it> |
| In reply to | #1627433 |
Hello,
On (04/20/17 15:11), Petr Mladek wrote:
[..]
> Good analyze. I would summarize it that we need to be careful of:
>
> + logbug_lock
> + PRINTK_SAFE_CONTEXT
> + locks used by console drivers
>
> The first two things are easy to check. Except that a check for logbuf_lock
> might produce false negatives. The last check is very hard.
>
> > so at the moment what I can think of is something like
> >
> > -- check this_cpu_read(printk_context) in NMI prink
> >
> > -- if we are NOT in printk_safe on this CPU, then do printk_deferred()
> > and bypass `nmi_print_seq' buffer
>
> I would add also a check for logbuf_lock.
> > -- if we are in printk_safe
> > -- well... bad luck... have a bigger buffer.
>
> Yup, we do the best effort while still trying to stay on the safe
> side.
>
> I have cooked up a patch based on this. It uses printk_deferred()
> in NMI when it is safe. Note that console_flush_on_panic() will
> try to get them on the console when a kdump is not generated.
> I believe that it will help Steven.
OK. I need to look more at the patch. It does more than I'd expected/imagined.
[..]
> void printk_nmi_enter(void)
> {
> - this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
> + /*
> + * The size of the extra per-CPU buffer is limited. Use it
> + * only when really needed.
> + */
> + if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK ||
> + raw_spin_is_locked(&logbuf_lock)) {
> + this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
> + } else {
> + this_cpu_or(printk_context, PRINTK_NMI_DEFERRED_CONTEXT_MASK);
> + }
> }
well... the logbuf_lock can temporarily be locked from another CPU. I'd say
that spin_is_locked() has better chances for false positive than
this_cpu_read(printk_context). because this_cpu_read(printk_context) depends
only on this CPU state, while spin_is_locked() depends on all CPUs. and the
idea with this_cpu_read(printk_context) was that we check if the logbuf_lock
was locked from this particular CPU.
I agree that this_cpu_read(printk_context) covers slightly more than
logbuf_lock scope, so we may get positive this_cpu_read(printk_context)
with unlocked logbuf_lock, but I don't tend to think that it's a big
problem.
wouldn't something as simple as below do the trick?
// absolutely and completely untested //
diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c
index 033e50a7d706..c7477654c5b1 100644
--- a/kernel/printk/printk_safe.c
+++ b/kernel/printk/printk_safe.c
@@ -303,7 +303,10 @@ static int vprintk_nmi(const char *fmt, va_list args)
{
struct printk_safe_seq_buf *s = this_cpu_ptr(&nmi_print_seq);
- return printk_safe_log_store(s, fmt, args);
+ if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
+ return printk_safe_log_store(s, fmt, args);
+
+ return vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
}
-ss
[toc] | [prev] | [next] | [standalone]
| From | Petr Mladek <pmladek@suse.com> |
|---|---|
| Date | 2017-04-21 14:10 +0200 |
| Message-ID | <tyFxT-5Ej-3@gated-at.bofh.it> |
| In reply to | #1627909 |
On Fri 2017-04-21 10:57:25, Sergey Senozhatsky wrote:
> On (04/20/17 15:11), Petr Mladek wrote:
> [..]
> > void printk_nmi_enter(void)
> > {
> > - this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
> > + /*
> > + * The size of the extra per-CPU buffer is limited. Use it
> > + * only when really needed.
> > + */
> > + if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK ||
> > + raw_spin_is_locked(&logbuf_lock)) {
> > + this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
> > + } else {
> > + this_cpu_or(printk_context, PRINTK_NMI_DEFERRED_CONTEXT_MASK);
> > + }
> > }
>
> well... the logbuf_lock can temporarily be locked from another CPU. I'd say
> that spin_is_locked() has better chances for false positive than
> this_cpu_read(printk_context). because this_cpu_read(printk_context) depends
> only on this CPU state, while spin_is_locked() depends on all CPUs. and the
> idea with this_cpu_read(printk_context) was that we check if the logbuf_lock
> was locked from this particular CPU.
I finally see the point. I confess that I did not think much about
this way yesterday because it looked too tricky (indirect) and possibly
error prone.
> I agree that this_cpu_read(printk_context) covers slightly more than
> logbuf_lock scope, so we may get positive this_cpu_read(printk_context)
> with unlocked logbuf_lock, but I don't tend to think that it's a big
> problem.
PRINTK_SAFE_CONTEXT is set also in call_console_drivers().
It might take rather long and logbuf_lock is availe. So, it is
noticeable source of false positives.
Hmm, my version actually checks both the lock and the context.
It is very deffensive to be always on the safe side.
We could get the best restults with both checks and by using "&&":
void printk_nmi_enter(void)
{
/*
* The size of the extra per-CPU buffer is limited. Use it
* only when the lock for the main log buffer is not
* available.
*
* logbuf_lock might be taken on another CPU. But it must be
* in PRINTK_SAFE context. Reduce false positives by a check
* of the context.
*/
if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK ||
raw_spin_is_locked(&logbuf_lock)) {
this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
} else {
this_cpu_or(printk_context, PRINTK_NMI_DEFERRED_CONTEXT_MASK);
}
}
But after all, I would prefer to keep it simple, strightforward,
and check only the logbuf_lock:
if (raw_spin_is_locked(&logbuf_lock))
this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
else
this_cpu_or(printk_context, PRINTK_NMI_DEFERRED_CONTEXT_MASK);
> wouldn't something as simple as below do the trick?
> // absolutely and completely untested //
>
>
> diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c
> index 033e50a7d706..c7477654c5b1 100644
> --- a/kernel/printk/printk_safe.c
> +++ b/kernel/printk/printk_safe.c
> @@ -303,7 +303,10 @@ static int vprintk_nmi(const char *fmt, va_list args)
> {
> struct printk_safe_seq_buf *s = this_cpu_ptr(&nmi_print_seq);
>
> - return printk_safe_log_store(s, fmt, args);
> + if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
> + return printk_safe_log_store(s, fmt, args);
> +
> + return vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
> }
It looks simple but some things are missing. It will be used also
outside panic/oops, so it should queue the irq_work to flush the console.
Also the serialization of nmi_cpu_backtrace() backtrace calls is
needed.
All in all, we could get rid only of the extra
PRINTK_NMI_DEFERRED_CONTEXT_MASK with this approach. Other than
that it looks more tricky to me.
Sigh, I hate problems without the single best solution.
Best Regards,
Petr
[toc] | [prev] | [next] | [standalone]
| From | Sergey Senozhatsky <sergey.senozhatsky.work@gmail.com> |
|---|---|
| Date | 2017-04-24 04:20 +0200 |
| Message-ID | <tzBLz-we-5@gated-at.bofh.it> |
| In reply to | #1628179 |
On (04/21/17 14:06), Petr Mladek wrote:
[..]
> > I agree that this_cpu_read(printk_context) covers slightly more than
> > logbuf_lock scope, so we may get positive this_cpu_read(printk_context)
> > with unlocked logbuf_lock, but I don't tend to think that it's a big
> > problem.
>
> PRINTK_SAFE_CONTEXT is set also in call_console_drivers().
> It might take rather long and logbuf_lock is availe. So, it is
> noticeable source of false positives.
yes, agree.
probably we need additional printk_safe annotations for
"logbuf_lock is locked from _this_ CPU"
false positives there can be very painful.
[..]
> if (raw_spin_is_locked(&logbuf_lock))
> this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
> else
> this_cpu_or(printk_context, PRINTK_NMI_DEFERRED_CONTEXT_MASK);
well, if everyone is fine with logbuf_lock access from every CPU from every
NMI then I won't object either. but may be it makes sense to reduce the
possibility of false positives. Steven is loosing critically important logs,
after all.
by the way,
does this `nmi_print_seq' bypass even fix anything for Steven? it sort of
can, in theory, but just in theory. so may be we need direct message flush
from NMI handler (printk->console_unlock), which will be a really big problem.
logbuf might not be big enough for 4890096 messages (Steven's report
mentions "Lost 4890096 message(s)!"). we are counting on the fact that
in case of `nmi_print_seq' bypass some other CPU will call console_unlock()
and print pending logbuf messages, but this is not guaranteed and the
messages can be dropped even from logbuf.
I don't know,
should we try to queue printk_deferred irq_work for all online CPUs from
vprintk_nmi() when it bypasses printk_safe_log_store()? in order to minimize
possibilities of logbuf overflow. printk_deferred() will queue work on
vprintk_nmi() CPU, sure, but we don't know how many messages we are going
to add to logbuf from NMI.
> > @@ -303,7 +303,10 @@ static int vprintk_nmi(const char *fmt, va_list args)
> > {
> > struct printk_safe_seq_buf *s = this_cpu_ptr(&nmi_print_seq);
> >
> > - return printk_safe_log_store(s, fmt, args);
> > + if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
> > + return printk_safe_log_store(s, fmt, args);
> > +
> > + return vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
> > }
>
> It looks simple but some things are missing. It will be used also
> outside panic/oops, so it should queue the irq_work to flush the console.
you are right. I thought about moving irq_work to vprintk_emit(), but
completely forgot about it. without that missing bit the proposed two-liner
is not complete.
-ss
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web