Skip to content

Commit 9a01c3e

Browse files
cmetcalf-tileratorvalds
authored andcommitted
nmi_backtrace: add more trigger_*_cpu_backtrace() methods
Patch series "improvements to the nmi_backtrace code" v9. This patch series modifies the trigger_xxx_backtrace() NMI-based remote backtracing code to make it more flexible, and makes a few small improvements along the way. The motivation comes from the task isolation code, where there are scenarios where we want to be able to diagnose a case where some cpu is about to interrupt a task-isolated cpu. It can be helpful to see both where the interrupting cpu is, and also an approximation of where the cpu that is being interrupted is. The nmi_backtrace framework allows us to discover the stack of the interrupted cpu. I've tested that the change works as desired on tile, and build-tested x86, arm, mips, and sparc64. For x86 I confirmed that the generic cpuidle stuff as well as the architecture-specific routines are in the new cpuidle section. For arm, mips, and sparc I just build-tested it and made sure the generic cpuidle routines were in the new cpuidle section, but I didn't attempt to figure out which the platform-specific idle routines might be. That might be more usefully done by someone with platform experience in follow-up patches. This patch (of 4): Currently you can only request a backtrace of either all cpus, or all cpus but yourself. It can also be helpful to request a remote backtrace of a single cpu, and since we want that, the logical extension is to support a cpumask as the underlying primitive. This change modifies the existing lib/nmi_backtrace.c code to take a cpumask as its basic primitive, and modifies the linux/nmi.h code to use the new "cpumask" method instead. The existing clients of nmi_backtrace (arm and x86) are converted to using the new cpumask approach in this change. The other users of the backtracing API (sparc64 and mips) are converted to use the cpumask approach rather than the all/allbutself approach. The mips code ignored the "include_self" boolean but with this change it will now also dump a local backtrace if requested. Link: http://lkml.kernel.org/r/1472487169-14923-2-git-send-email-cmetcalf@mellanox.com Signed-off-by: Chris Metcalf <cmetcalf@mellanox.com> Tested-by: Daniel Thompson <daniel.thompson@linaro.org> [arm] Reviewed-by: Aaron Tomlin <atomlin@redhat.com> Reviewed-by: Petr Mladek <pmladek@suse.com> Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net> Cc: Russell King <linux@arm.linux.org.uk> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@elte.hu> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: David Miller <davem@davemloft.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 589a978 commit 9a01c3e

File tree

10 files changed

+72
-39
lines changed

10 files changed

+72
-39
lines changed

arch/arm/include/asm/irq.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
3636
#endif
3737

3838
#ifdef CONFIG_SMP
39-
extern void arch_trigger_all_cpu_backtrace(bool);
40-
#define arch_trigger_all_cpu_backtrace(x) arch_trigger_all_cpu_backtrace(x)
39+
extern void arch_trigger_cpumask_backtrace(const cpumask_t *mask,
40+
bool exclude_self);
41+
#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
4142
#endif
4243

4344
static inline int nr_legacy_irqs(void)

arch/arm/kernel/smp.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ static void raise_nmi(cpumask_t *mask)
760760
smp_cross_call(mask, IPI_CPU_BACKTRACE);
761761
}
762762

763-
void arch_trigger_all_cpu_backtrace(bool include_self)
763+
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
764764
{
765-
nmi_trigger_all_cpu_backtrace(include_self, raise_nmi);
765+
nmi_trigger_cpumask_backtrace(mask, exclude_self, raise_nmi);
766766
}

arch/mips/include/asm/irq.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ extern int cp0_fdc_irq;
5151

5252
extern int get_c0_fdc_int(void);
5353

54-
void arch_trigger_all_cpu_backtrace(bool);
55-
#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace
54+
void arch_trigger_cpumask_backtrace(const struct cpumask *mask,
55+
bool exclude_self);
56+
#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
5657

5758
#endif /* _ASM_IRQ_H */

arch/mips/kernel/process.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -569,9 +569,16 @@ static void arch_dump_stack(void *info)
569569
dump_stack();
570570
}
571571

572-
void arch_trigger_all_cpu_backtrace(bool include_self)
572+
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
573573
{
574-
smp_call_function(arch_dump_stack, NULL, 1);
574+
long this_cpu = get_cpu();
575+
576+
if (cpumask_test_cpu(this_cpu, mask) && !exclude_self)
577+
dump_stack();
578+
579+
smp_call_function_many(mask, arch_dump_stack, NULL, 1);
580+
581+
put_cpu();
575582
}
576583

577584
int mips_get_process_fp_mode(struct task_struct *task)

arch/sparc/include/asm/irq_64.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ static inline unsigned long get_softint(void)
8686
return retval;
8787
}
8888

89-
void arch_trigger_all_cpu_backtrace(bool);
90-
#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace
89+
void arch_trigger_cpumask_backtrace(const struct cpumask *mask,
90+
bool exclude_self);
91+
#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
9192

9293
extern void *hardirq_stack[NR_CPUS];
9394
extern void *softirq_stack[NR_CPUS];

arch/sparc/kernel/process_64.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ static void __global_reg_poll(struct global_reg_snapshot *gp)
239239
}
240240
}
241241

242-
void arch_trigger_all_cpu_backtrace(bool include_self)
242+
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
243243
{
244244
struct thread_info *tp = current_thread_info();
245245
struct pt_regs *regs = get_irq_regs();
@@ -255,15 +255,15 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
255255

256256
memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
257257

258-
if (include_self)
258+
if (cpumask_test_cpu(this_cpu, mask) && !exclude_self)
259259
__global_reg_self(tp, regs, this_cpu);
260260

261261
smp_fetch_global_regs();
262262

263-
for_each_online_cpu(cpu) {
263+
for_each_cpu(cpu, mask) {
264264
struct global_reg_snapshot *gp;
265265

266-
if (!include_self && cpu == this_cpu)
266+
if (exclude_self && cpu == this_cpu)
267267
continue;
268268

269269
gp = &global_cpu_snapshot[cpu].reg;
@@ -300,7 +300,7 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
300300

301301
static void sysrq_handle_globreg(int key)
302302
{
303-
arch_trigger_all_cpu_backtrace(true);
303+
trigger_all_cpu_backtrace();
304304
}
305305

306306
static struct sysrq_key_op sparc_globalreg_op = {

arch/x86/include/asm/irq.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ extern int vector_used_by_percpu_irq(unsigned int vector);
5050
extern void init_ISA_irqs(void);
5151

5252
#ifdef CONFIG_X86_LOCAL_APIC
53-
void arch_trigger_all_cpu_backtrace(bool);
54-
#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace
53+
void arch_trigger_cpumask_backtrace(const struct cpumask *mask,
54+
bool exclude_self);
55+
#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
5556
#endif
5657

5758
#endif /* _ASM_X86_IRQ_H */

arch/x86/kernel/apic/hw_nmi.c

+9-9
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,32 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh)
2626
}
2727
#endif
2828

29-
#ifdef arch_trigger_all_cpu_backtrace
29+
#ifdef arch_trigger_cpumask_backtrace
3030
static void nmi_raise_cpu_backtrace(cpumask_t *mask)
3131
{
3232
apic->send_IPI_mask(mask, NMI_VECTOR);
3333
}
3434

35-
void arch_trigger_all_cpu_backtrace(bool include_self)
35+
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
3636
{
37-
nmi_trigger_all_cpu_backtrace(include_self, nmi_raise_cpu_backtrace);
37+
nmi_trigger_cpumask_backtrace(mask, exclude_self,
38+
nmi_raise_cpu_backtrace);
3839
}
3940

40-
static int
41-
arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
41+
static int nmi_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
4242
{
4343
if (nmi_cpu_backtrace(regs))
4444
return NMI_HANDLED;
4545

4646
return NMI_DONE;
4747
}
48-
NOKPROBE_SYMBOL(arch_trigger_all_cpu_backtrace_handler);
48+
NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler);
4949

50-
static int __init register_trigger_all_cpu_backtrace(void)
50+
static int __init register_nmi_cpu_backtrace_handler(void)
5151
{
52-
register_nmi_handler(NMI_LOCAL, arch_trigger_all_cpu_backtrace_handler,
52+
register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler,
5353
0, "arch_bt");
5454
return 0;
5555
}
56-
early_initcall(register_trigger_all_cpu_backtrace);
56+
early_initcall(register_nmi_cpu_backtrace_handler);
5757
#endif

include/linux/nmi.h

+26-5
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,34 @@ static inline void hardlockup_detector_disable(void) {}
3535
* base function. Return whether such support was available,
3636
* to allow calling code to fall back to some other mechanism:
3737
*/
38-
#ifdef arch_trigger_all_cpu_backtrace
38+
#ifdef arch_trigger_cpumask_backtrace
3939
static inline bool trigger_all_cpu_backtrace(void)
4040
{
41-
arch_trigger_all_cpu_backtrace(true);
42-
41+
arch_trigger_cpumask_backtrace(cpu_online_mask, false);
4342
return true;
4443
}
44+
4545
static inline bool trigger_allbutself_cpu_backtrace(void)
4646
{
47-
arch_trigger_all_cpu_backtrace(false);
47+
arch_trigger_cpumask_backtrace(cpu_online_mask, true);
48+
return true;
49+
}
50+
51+
static inline bool trigger_cpumask_backtrace(struct cpumask *mask)
52+
{
53+
arch_trigger_cpumask_backtrace(mask, false);
54+
return true;
55+
}
56+
57+
static inline bool trigger_single_cpu_backtrace(int cpu)
58+
{
59+
arch_trigger_cpumask_backtrace(cpumask_of(cpu), false);
4860
return true;
4961
}
5062

5163
/* generic implementation */
52-
void nmi_trigger_all_cpu_backtrace(bool include_self,
64+
void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
65+
bool exclude_self,
5366
void (*raise)(cpumask_t *mask));
5467
bool nmi_cpu_backtrace(struct pt_regs *regs);
5568

@@ -62,6 +75,14 @@ static inline bool trigger_allbutself_cpu_backtrace(void)
6275
{
6376
return false;
6477
}
78+
static inline bool trigger_cpumask_backtrace(struct cpumask *mask)
79+
{
80+
return false;
81+
}
82+
static inline bool trigger_single_cpu_backtrace(int cpu)
83+
{
84+
return false;
85+
}
6586
#endif
6687

6788
#ifdef CONFIG_LOCKUP_DETECTOR

lib/nmi_backtrace.c

+9-8
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,21 @@
1717
#include <linux/kprobes.h>
1818
#include <linux/nmi.h>
1919

20-
#ifdef arch_trigger_all_cpu_backtrace
20+
#ifdef arch_trigger_cpumask_backtrace
2121
/* For reliability, we're prepared to waste bits here. */
2222
static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly;
2323

24-
/* "in progress" flag of arch_trigger_all_cpu_backtrace */
24+
/* "in progress" flag of arch_trigger_cpumask_backtrace */
2525
static unsigned long backtrace_flag;
2626

2727
/*
28-
* When raise() is called it will be is passed a pointer to the
28+
* When raise() is called it will be passed a pointer to the
2929
* backtrace_mask. Architectures that call nmi_cpu_backtrace()
3030
* directly from their raise() functions may rely on the mask
3131
* they are passed being updated as a side effect of this call.
3232
*/
33-
void nmi_trigger_all_cpu_backtrace(bool include_self,
33+
void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
34+
bool exclude_self,
3435
void (*raise)(cpumask_t *mask))
3536
{
3637
int i, this_cpu = get_cpu();
@@ -44,13 +45,13 @@ void nmi_trigger_all_cpu_backtrace(bool include_self,
4445
return;
4546
}
4647

47-
cpumask_copy(to_cpumask(backtrace_mask), cpu_online_mask);
48-
if (!include_self)
48+
cpumask_copy(to_cpumask(backtrace_mask), mask);
49+
if (exclude_self)
4950
cpumask_clear_cpu(this_cpu, to_cpumask(backtrace_mask));
5051

5152
if (!cpumask_empty(to_cpumask(backtrace_mask))) {
52-
pr_info("Sending NMI to %s CPUs:\n",
53-
(include_self ? "all" : "other"));
53+
pr_info("Sending NMI from CPU %d to CPUs %*pbl:\n",
54+
this_cpu, nr_cpumask_bits, to_cpumask(backtrace_mask));
5455
raise(to_cpumask(backtrace_mask));
5556
}
5657

0 commit comments

Comments
 (0)