From: Anton Blanchard <hidden> Date: 2015-02-24 03:30:39
Every now and then I end up with an undebuggable issue because multiple
CPUs hit something at the same time and everything is interleaved:
CR: 48000082 XER: 00000000
,RI
c0000003dc72fd10
,LE
d0000000065b84e8
Instruction dump:
MSR: 8000000100029033
Very annoying.
Some architectures already have their own recursive locking for oopses
and we have another version for serialising dump_stack.
Create a common version and use it everywhere (oopses, BUGs, WARNs,
dump_stack, soft lockups and hard lockups). A few testcases were
used to verify the series:
A trivial module to create concurrent WARNs, BUGs and oopses:
http://ozlabs.org/~anton/junkcode/warnstorm.tar.gz
And one to create concurrent soft and hard lockups:
http://ozlabs.org/~anton/junkcode/badguy.tar.gz
Anton Blanchard (7):
Add die_spin_lock_{irqsave,irqrestore}
powerpc: Use die_spin_lock_{irqsave,irqrestore}
arm: Use die_spin_lock_{irqsave,irqrestore}
x86: Use die_spin_lock_{irqsave,irqrestore}
watchdog: Serialise soft lockup errors with
die_spin_lock_{irqsave,irqrestore}
dump_stack: Serialise dump_stack with
die_spin_lock_{irqsave,irqrestore}
powerpc: Serialise BUG and WARNs with
die_spin_lock_{irqsave,irqrestore}
arch/arm/kernel/traps.c | 26 ++---------------
arch/powerpc/kernel/traps.c | 68 ++++++++++++++++++++++++++-------------------
arch/x86/kernel/dumpstack.c | 26 ++---------------
include/linux/die_lock.h | 23 +++++++++++++++
kernel/watchdog.c | 4 +++
lib/Makefile | 1 +
lib/die_lock.c | 43 ++++++++++++++++++++++++++++
lib/dump_stack.c | 40 +++-----------------------
8 files changed, 120 insertions(+), 111 deletions(-)
create mode 100644 include/linux/die_lock.h
create mode 100644 lib/die_lock.c
--
2.1.0
From: Anton Blanchard <hidden> Date: 2015-02-24 03:30:44
Many architectures have their own oops locking code that allows
the lock to be taken recursively. Create a common version.
Avoid creating generic locking functions, so they can't be
abused in other parts of the kernel.
Signed-off-by: Anton Blanchard <redacted>
---
include/linux/die_lock.h | 23 +++++++++++++++++++++++
lib/Makefile | 1 +
lib/die_lock.c | 43 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+)
create mode 100644 include/linux/die_lock.h
create mode 100644 lib/die_lock.c
From: Anton Blanchard <hidden> Date: 2015-02-24 03:30:48
Replace the ARM specific oops locking with the common one.
Signed-off-by: Anton Blanchard <redacted>
---
arch/arm/kernel/traps.c | 26 +++-----------------------
1 file changed, 3 insertions(+), 23 deletions(-)
@@ -263,28 +264,12 @@ static int __die(const char *str, int err, struct pt_regs *regs)return0;}-staticarch_spinlock_tdie_lock=__ARCH_SPIN_LOCK_UNLOCKED;-staticintdie_owner=-1;-staticunsignedintdie_nest_count;-staticunsignedlongoops_begin(void){-intcpu;unsignedlongflags;oops_enter();--/* racy, but better than risking deadlock. */-raw_local_irq_save(flags);-cpu=smp_processor_id();-if(!arch_spin_trylock(&die_lock)){-if(cpu==die_owner)-/* nested oops. should stop eventually */;-else-arch_spin_lock(&die_lock);-}-die_nest_count++;-die_owner=cpu;+die_spin_lock_irqsave(flags);console_verbose();bust_spinlocks(1);returnflags;
@@ -296,13 +281,8 @@ static void oops_end(unsigned long flags, struct pt_regs *regs, int signr)crash_kexec(regs);bust_spinlocks(0);-die_owner=-1;add_taint(TAINT_DIE,LOCKDEP_NOW_UNRELIABLE);-die_nest_count--;-if(!die_nest_count)-/* Nest count reaches zero, release the lock. */-arch_spin_unlock(&die_lock);-raw_local_irq_restore(flags);+die_spin_unlock_irqrestore(flags);oops_exit();if(in_interrupt())
From: Anton Blanchard <hidden> Date: 2015-02-24 03:30:51
Replace the x86 specific oops locking with the common one.
Signed-off-by: Anton Blanchard <redacted>
---
arch/x86/kernel/dumpstack.c | 26 +++-----------------------
1 file changed, 3 insertions(+), 23 deletions(-)
@@ -196,28 +197,12 @@ void show_stack(struct task_struct *task, unsigned long *sp)show_stack_log_lvl(task,NULL,sp,bp,"");}-staticarch_spinlock_tdie_lock=__ARCH_SPIN_LOCK_UNLOCKED;-staticintdie_owner=-1;-staticunsignedintdie_nest_count;-unsignedlongoops_begin(void){-intcpu;unsignedlongflags;oops_enter();--/* racy, but better than risking deadlock. */-raw_local_irq_save(flags);-cpu=smp_processor_id();-if(!arch_spin_trylock(&die_lock)){-if(cpu==die_owner)-/* nested oops. should stop eventually */;-else-arch_spin_lock(&die_lock);-}-die_nest_count++;-die_owner=cpu;+die_spin_lock_irqsave(flags);console_verbose();bust_spinlocks(1);returnflags;
@@ -231,13 +216,8 @@ void oops_end(unsigned long flags, struct pt_regs *regs, int signr)crash_kexec(regs);bust_spinlocks(0);-die_owner=-1;add_taint(TAINT_DIE,LOCKDEP_NOW_UNRELIABLE);-die_nest_count--;-if(!die_nest_count)-/* Nest count reaches zero, release the lock. */-arch_spin_unlock(&die_lock);-raw_local_irq_restore(flags);+die_spin_unlock_irqrestore(flags);oops_exit();if(!signr)
From: Anton Blanchard <hidden> Date: 2015-02-24 03:30:53
A simple kernel module was used to create concurrent soft and
hard lockups:
http://ozlabs.org/~anton/junkcode/badguy.tar.gz
Signed-off-by: Anton Blanchard <redacted>
---
kernel/watchdog.c | 4 ++++
1 file changed, 4 insertions(+)
@@ -394,6 +397,7 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)show_regs(regs);elsedump_stack();+die_spin_unlock_irqrestore(flags);if(softlockup_all_cpu_backtrace){/* Avoid generating two back traces for current
From: Anton Blanchard <hidden> Date: 2015-02-24 03:30:55
Remove another version of a recursive lock in dump_stack.
Signed-off-by: Anton Blanchard <redacted>
---
lib/dump_stack.c | 40 ++++------------------------------------
1 file changed, 4 insertions(+), 36 deletions(-)
From: Anton Blanchard <hidden> Date: 2015-02-24 03:30:56
A simple kernel module was used to create concurrent WARNs and BUGs:
http://ozlabs.org/~anton/junkcode/warnstorm.tar.gz
Signed-off-by: Anton Blanchard <redacted>
---
arch/powerpc/kernel/traps.c | 44 ++++++++++++++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 8 deletions(-)
From: Anton Blanchard <hidden> Date: 2015-02-24 03:32:08
Replace the powerpc specific oops locking with the common one.
Signed-off-by: Anton Blanchard <redacted>
---
arch/powerpc/kernel/traps.c | 24 +++---------------------
1 file changed, 3 insertions(+), 21 deletions(-)
@@ -124,17 +121,7 @@ static unsigned __kprobes long oops_begin(struct pt_regs *regs)oops_enter();-/* racy, but better than risking deadlock. */-raw_local_irq_save(flags);-cpu=smp_processor_id();-if(!arch_spin_trylock(&die_lock)){-if(cpu==die_owner)-/* nested oops. should stop eventually */;-else-arch_spin_lock(&die_lock);-}-die_nest_count++;-die_owner=cpu;+die_spin_lock_irqsave(flags);console_verbose();bust_spinlocks(1);if(machine_is(powermac))
Every now and then I end up with an undebuggable issue
because multiple CPUs hit something at the same time and
everything is interleaved:
CR: 48000082 XER: 00000000
,RI
c0000003dc72fd10
,LE
d0000000065b84e8
Instruction dump:
MSR: 8000000100029033
Very annoying.
Some architectures already have their own recursive
locking for oopses and we have another version for
serialising dump_stack.
Create a common version and use it everywhere (oopses,
BUGs, WARNs, dump_stack, soft lockups and hard lockups).
Dunno. I've had cases where the simultaneity of the oopses
(i.e. their garbled nature) gave me the clue about the type
of race to expect.
To still get that information: instead of taking a
serializing spinlock (or in addition to it), it would be
nice to at least preserve the true time order of the
incidents, at minimum by generating a global count for
oopses/warnings (a bit like the oops count # currently),
and to gather it first - before taking any spinlocks.
Thanks,
Ingo
+static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+static int die_owner = -1;
+static unsigned int die_nest_count;
+
+unsigned long __die_spin_lock_irqsave(void)
+{
+ unsigned long flags;
+ int cpu;
+
+ /* racy, but better than risking deadlock. */
+ raw_local_irq_save(flags);
+
+ cpu = smp_processor_id();
+ if (!arch_spin_trylock(&die_lock)) {
+ if (cpu != die_owner)
+ arch_spin_lock(&die_lock);
So why not trylock and time out here after a few seconds,
instead of indefinitely supressing some potentially vital
output due to some other CPU crashing/locking with the lock
held?
I suspect this would work in most cases.
If we fix the deadlock potential, and get a true global
ordering of various oopses/warnings as they triggered (or
at least timestamping them), then I'm sold on this I guess,
it will likely improve things.
Thanks,
Ingo
+static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+static int die_owner = -1;
+static unsigned int die_nest_count;
+
+unsigned long __die_spin_lock_irqsave(void)
+{
+ unsigned long flags;
+ int cpu;
+
+ /* racy, but better than risking deadlock. */
+ raw_local_irq_save(flags);
+
+ cpu = smp_processor_id();
+ if (!arch_spin_trylock(&die_lock)) {
+ if (cpu != die_owner)
+ arch_spin_lock(&die_lock);
So why not trylock and time out here after a few seconds,
instead of indefinitely supressing some potentially vital
output due to some other CPU crashing/locking with the lock
held?
[...]
If we fix the deadlock potential, and get a true global
ordering of various oopses/warnings as they triggered (or
at least timestamping them), [...]
If we had a global 'trouble counter' we could use that to
refine the spin-looping timeout: instead of using a pure
timeout of a few seconds, we could say 'a timeout of a few
seconds while the counter does not increase'.
I.e. only override the locking/ordering if the owner CPU
does not seem to be able to make progress with printing the
oops/warning.
Thanks,
Ingo
From: Arjan van de Ven <hidden> Date: 2015-02-24 09:39:53
quoted
Some architectures already have their own recursive
locking for oopses and we have another version for
serialising dump_stack.
Create a common version and use it everywhere (oopses,
BUGs, WARNs, dump_stack, soft lockups and hard lockups).
Dunno. I've had cases where the simultaneity of the oopses
(i.e. their garbled nature) gave me the clue about the type
of race to expect.
one of the question is if you want to serialize, or if you just want to label.
If you take a cookie (could just be a monotonic increasing number) at
the start of the oops
and then prefix/postfix the stack printing with that number, you don't
serialize (risk of locking up),
but you can pretty trivially see which line came from where..
if you do the monotonic increasing number approach, you even get an
ordering out of it.
it does mean changing the dump_stack() and co function fingerprint to
take an extra argument,
but that is not TOO insane.
From: Russell King - ARM Linux <hidden> Date: 2015-02-24 10:44:44
On Tue, Feb 24, 2015 at 01:39:46AM -0800, Arjan van de Ven wrote:
one of the question is if you want to serialize, or if you just want
to label. If you take a cookie (could just be a monotonic increasing
number) at the start of the oops and then prefix/postfix the stack
printing with that number, you don't serialize (risk of locking up),
but you can pretty trivially see which line came from where..
if you do the monotonic increasing number approach, you even get an
ordering out of it. it does mean changing the dump_stack() and co
function fingerprint to take an extra argument, but that is not TOO
insane.
I like that idea, but it relies on ensuring that each line is printed
by one printk() statement - which in itself is a good idea.
I'd actually like a version of print_hex_dump() which we could use for
stack and code dumping - the existing print_hex_dump() assumes that it's
fine to dereference the pointer, whereas for stack and code dumping,
we can't always make that assumption. That's a separate issue though.
--
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.