Skip to content

Commit b3e15bd

Browse files
aristeuIngo Molnar
authored andcommitted
x86, NMI watchdog: setup before enabling NMI watchdog
There's a small window when NMI watchdog is being set up that if any NMIs are triggered, the NMI code will make make use of not initalized wd_ops elements: void setup_apic_nmi_watchdog(void *unused) { if (__get_cpu_var(wd_enabled)) return; /* cheap hack to support suspend/resume */ /* if cpu0 is not active neither should the other cpus */ if (smp_processor_id() != 0 && atomic_read(&nmi_active) <= 0) return; switch (nmi_watchdog) { case NMI_LOCAL_APIC: /* enable it before to avoid race with handler */ --> __get_cpu_var(wd_enabled) = 1; --> if (lapic_watchdog_init(nmi_hz) < 0) { (...) asmlinkage notrace __kprobes void default_do_nmi(struct pt_regs *regs) { (...) if (nmi_watchdog_tick(regs, reason)) return; (...) notrace __kprobes int nmi_watchdog_tick(struct pt_regs *regs, unsigned reason) { (...) if (!__get_cpu_var(wd_enabled)) return rc; switch (nmi_watchdog) { case NMI_LOCAL_APIC: rc |= lapic_wd_event(nmi_hz); (...) int lapic_wd_event(unsigned nmi_hz) { struct nmi_watchdog_ctlblk *wd = &__get_cpu_var(nmi_watchdog_ctlblk); u64 ctr; --> rdmsrl(wd->perfctr_msr, ctr); and wd->*_msr will be initialized on each processor type specific setup, after enabling NMIs for PMIs. Since the counter was just set, the chances of an performance counter generated NMI is minimal, but any other unknown NMI would trigger the problem. This patch fixes the problem by setting everything up before enabling performance counter generated NMIs and will set wd_enabled using a callback function. Signed-off-by: Aristeu Rozanski <[email protected]> Acked-by: Don Zickus <[email protected]> Acked-by: Prarit Bhargava <[email protected]> Acked-by: Vivek Goyal <[email protected]> Signed-off-by: Ingo Molnar <[email protected]>
1 parent 28b166a commit b3e15bd

File tree

3 files changed

+43
-14
lines changed

3 files changed

+43
-14
lines changed

arch/x86/kernel/cpu/perfctr-watchdog.c

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,19 @@ static int setup_k7_watchdog(unsigned nmi_hz)
295295
/* setup the timer */
296296
wrmsr(evntsel_msr, evntsel, 0);
297297
write_watchdog_counter(perfctr_msr, "K7_PERFCTR0",nmi_hz);
298-
apic_write(APIC_LVTPC, APIC_DM_NMI);
299-
evntsel |= K7_EVNTSEL_ENABLE;
300-
wrmsr(evntsel_msr, evntsel, 0);
301298

299+
/* initialize the wd struct before enabling */
302300
wd->perfctr_msr = perfctr_msr;
303301
wd->evntsel_msr = evntsel_msr;
304302
wd->cccr_msr = 0; /* unused */
303+
304+
/* ok, everything is initialized, announce that we're set */
305+
cpu_nmi_set_wd_enabled();
306+
307+
apic_write(APIC_LVTPC, APIC_DM_NMI);
308+
evntsel |= K7_EVNTSEL_ENABLE;
309+
wrmsr(evntsel_msr, evntsel, 0);
310+
305311
return 1;
306312
}
307313

@@ -379,13 +385,19 @@ static int setup_p6_watchdog(unsigned nmi_hz)
379385
wrmsr(evntsel_msr, evntsel, 0);
380386
nmi_hz = adjust_for_32bit_ctr(nmi_hz);
381387
write_watchdog_counter32(perfctr_msr, "P6_PERFCTR0",nmi_hz);
382-
apic_write(APIC_LVTPC, APIC_DM_NMI);
383-
evntsel |= P6_EVNTSEL0_ENABLE;
384-
wrmsr(evntsel_msr, evntsel, 0);
385388

389+
/* initialize the wd struct before enabling */
386390
wd->perfctr_msr = perfctr_msr;
387391
wd->evntsel_msr = evntsel_msr;
388392
wd->cccr_msr = 0; /* unused */
393+
394+
/* ok, everything is initialized, announce that we're set */
395+
cpu_nmi_set_wd_enabled();
396+
397+
apic_write(APIC_LVTPC, APIC_DM_NMI);
398+
evntsel |= P6_EVNTSEL0_ENABLE;
399+
wrmsr(evntsel_msr, evntsel, 0);
400+
389401
return 1;
390402
}
391403

@@ -540,12 +552,17 @@ static int setup_p4_watchdog(unsigned nmi_hz)
540552
wrmsr(evntsel_msr, evntsel, 0);
541553
wrmsr(cccr_msr, cccr_val, 0);
542554
write_watchdog_counter(perfctr_msr, "P4_IQ_COUNTER0", nmi_hz);
543-
apic_write(APIC_LVTPC, APIC_DM_NMI);
544-
cccr_val |= P4_CCCR_ENABLE;
545-
wrmsr(cccr_msr, cccr_val, 0);
555+
546556
wd->perfctr_msr = perfctr_msr;
547557
wd->evntsel_msr = evntsel_msr;
548558
wd->cccr_msr = cccr_msr;
559+
560+
/* ok, everything is initialized, announce that we're set */
561+
cpu_nmi_set_wd_enabled();
562+
563+
apic_write(APIC_LVTPC, APIC_DM_NMI);
564+
cccr_val |= P4_CCCR_ENABLE;
565+
wrmsr(cccr_msr, cccr_val, 0);
549566
return 1;
550567
}
551568

@@ -661,13 +678,17 @@ static int setup_intel_arch_watchdog(unsigned nmi_hz)
661678
wrmsr(evntsel_msr, evntsel, 0);
662679
nmi_hz = adjust_for_32bit_ctr(nmi_hz);
663680
write_watchdog_counter32(perfctr_msr, "INTEL_ARCH_PERFCTR0", nmi_hz);
664-
apic_write(APIC_LVTPC, APIC_DM_NMI);
665-
evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE;
666-
wrmsr(evntsel_msr, evntsel, 0);
667681

668682
wd->perfctr_msr = perfctr_msr;
669683
wd->evntsel_msr = evntsel_msr;
670684
wd->cccr_msr = 0; /* unused */
685+
686+
/* ok, everything is initialized, announce that we're set */
687+
cpu_nmi_set_wd_enabled();
688+
689+
apic_write(APIC_LVTPC, APIC_DM_NMI);
690+
evntsel |= ARCH_PERFMON_EVENTSEL0_ENABLE;
691+
wrmsr(evntsel_msr, evntsel, 0);
671692
intel_arch_wd_ops.checkbit = 1ULL << (eax.split.bit_width - 1);
672693
return 1;
673694
}

arch/x86/kernel/nmi.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,15 @@ void acpi_nmi_disable(void)
299299
on_each_cpu(__acpi_nmi_disable, NULL, 1);
300300
}
301301

302+
/*
303+
* This function is called as soon the LAPIC NMI watchdog driver has everything
304+
* in place and it's ready to check if the NMIs belong to the NMI watchdog
305+
*/
306+
void cpu_nmi_set_wd_enabled(void)
307+
{
308+
__get_cpu_var(wd_enabled) = 1;
309+
}
310+
302311
void setup_apic_nmi_watchdog(void *unused)
303312
{
304313
if (__get_cpu_var(wd_enabled))
@@ -311,8 +320,6 @@ void setup_apic_nmi_watchdog(void *unused)
311320

312321
switch (nmi_watchdog) {
313322
case NMI_LOCAL_APIC:
314-
/* enable it before to avoid race with handler */
315-
__get_cpu_var(wd_enabled) = 1;
316323
if (lapic_watchdog_init(nmi_hz) < 0) {
317324
__get_cpu_var(wd_enabled) = 0;
318325
return;

include/asm-x86/nmi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ extern void stop_apic_nmi_watchdog(void *);
3434
extern void disable_timer_nmi_watchdog(void);
3535
extern void enable_timer_nmi_watchdog(void);
3636
extern int nmi_watchdog_tick(struct pt_regs *regs, unsigned reason);
37+
extern void cpu_nmi_set_wd_enabled(void);
3738

3839
extern atomic_t nmi_active;
3940
extern unsigned int nmi_watchdog;

0 commit comments

Comments
 (0)