Commit 93dbf6d6 authored by Mikael Pettersson's avatar Mikael Pettersson Committed by Linus Torvalds

[PATCH] allow drivers to claim the lapic NMI watchdog HW

Here is an updated lapic NMI ownership tracking patch which
should address the issues that were raised with the first one:

- Simplified the API function names to {reserve,release}_lapic_nmi().

- Rewrote the ownership tracking code to use two individually named
  flags instead of using arithmetic and the sign. The code is now
  simple enough that no "hiding" macros are needed. (Thanks Albert
  for that suggestion.)
parent 1a4c6c5d
...@@ -36,6 +36,20 @@ static unsigned int nmi_hz = HZ; ...@@ -36,6 +36,20 @@ static unsigned int nmi_hz = HZ;
unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */
extern void show_registers(struct pt_regs *regs); extern void show_registers(struct pt_regs *regs);
/*
* lapic_nmi_owner tracks the ownership of the lapic NMI hardware:
* - it may be reserved by some other driver, or not
* - when not reserved by some other driver, it may be used for
* the NMI watchdog, or not
*
* This is maintained separately from nmi_active because the NMI
* watchdog may also be driven from the I/O APIC timer.
*/
static spinlock_t lapic_nmi_owner_lock = SPIN_LOCK_UNLOCKED;
static unsigned int lapic_nmi_owner;
#define LAPIC_NMI_WATCHDOG (1<<0)
#define LAPIC_NMI_RESERVED (1<<1)
/* nmi_active: /* nmi_active:
* +1: the lapic NMI watchdog is active, but can be disabled * +1: the lapic NMI watchdog is active, but can be disabled
* 0: the lapic NMI watchdog has not been set up, and cannot * 0: the lapic NMI watchdog has not been set up, and cannot
...@@ -102,6 +116,7 @@ int __init check_nmi_watchdog (void) ...@@ -102,6 +116,7 @@ int __init check_nmi_watchdog (void)
if (nmi_count(cpu) - prev_nmi_count[cpu] <= 5) { if (nmi_count(cpu) - prev_nmi_count[cpu] <= 5) {
printk("CPU#%d: NMI appears to be stuck!\n", cpu); printk("CPU#%d: NMI appears to be stuck!\n", cpu);
nmi_active = 0; nmi_active = 0;
lapic_nmi_owner &= ~LAPIC_NMI_WATCHDOG;
return -1; return -1;
} }
} }
...@@ -151,7 +166,7 @@ static int __init setup_nmi_watchdog(char *str) ...@@ -151,7 +166,7 @@ static int __init setup_nmi_watchdog(char *str)
__setup("nmi_watchdog=", setup_nmi_watchdog); __setup("nmi_watchdog=", setup_nmi_watchdog);
void disable_lapic_nmi_watchdog(void) static void disable_lapic_nmi_watchdog(void)
{ {
if (nmi_active <= 0) if (nmi_active <= 0)
return; return;
...@@ -182,7 +197,7 @@ void disable_lapic_nmi_watchdog(void) ...@@ -182,7 +197,7 @@ void disable_lapic_nmi_watchdog(void)
nmi_watchdog = 0; nmi_watchdog = 0;
} }
void enable_lapic_nmi_watchdog(void) static void enable_lapic_nmi_watchdog(void)
{ {
if (nmi_active < 0) { if (nmi_active < 0) {
nmi_watchdog = NMI_LOCAL_APIC; nmi_watchdog = NMI_LOCAL_APIC;
...@@ -190,6 +205,33 @@ void enable_lapic_nmi_watchdog(void) ...@@ -190,6 +205,33 @@ void enable_lapic_nmi_watchdog(void)
} }
} }
int reserve_lapic_nmi(void)
{
unsigned int old_owner;
spin_lock(&lapic_nmi_owner_lock);
old_owner = lapic_nmi_owner;
lapic_nmi_owner |= LAPIC_NMI_RESERVED;
spin_unlock(&lapic_nmi_owner_lock);
if (old_owner & LAPIC_NMI_RESERVED)
return -EBUSY;
if (old_owner & LAPIC_NMI_WATCHDOG)
disable_lapic_nmi_watchdog();
return 0;
}
void release_lapic_nmi(void)
{
unsigned int new_owner;
spin_lock(&lapic_nmi_owner_lock);
new_owner = lapic_nmi_owner & ~LAPIC_NMI_RESERVED;
lapic_nmi_owner = new_owner;
spin_unlock(&lapic_nmi_owner_lock);
if (new_owner & LAPIC_NMI_WATCHDOG)
enable_lapic_nmi_watchdog();
}
void disable_timer_nmi_watchdog(void) void disable_timer_nmi_watchdog(void)
{ {
if ((nmi_watchdog != NMI_IO_APIC) || (nmi_active <= 0)) if ((nmi_watchdog != NMI_IO_APIC) || (nmi_active <= 0))
...@@ -243,7 +285,7 @@ static int __init init_lapic_nmi_sysfs(void) ...@@ -243,7 +285,7 @@ static int __init init_lapic_nmi_sysfs(void)
{ {
int error; int error;
if (nmi_active == 0) if (nmi_active == 0 || nmi_watchdog != NMI_LOCAL_APIC)
return 0; return 0;
error = sysdev_class_register(&nmi_sysclass); error = sysdev_class_register(&nmi_sysclass);
...@@ -373,6 +415,7 @@ void setup_apic_nmi_watchdog (void) ...@@ -373,6 +415,7 @@ void setup_apic_nmi_watchdog (void)
default: default:
return; return;
} }
lapic_nmi_owner = LAPIC_NMI_WATCHDOG;
nmi_active = 1; nmi_active = 1;
} }
...@@ -470,7 +513,7 @@ void nmi_watchdog_tick (struct pt_regs * regs) ...@@ -470,7 +513,7 @@ void nmi_watchdog_tick (struct pt_regs * regs)
EXPORT_SYMBOL(nmi_active); EXPORT_SYMBOL(nmi_active);
EXPORT_SYMBOL(nmi_watchdog); EXPORT_SYMBOL(nmi_watchdog);
EXPORT_SYMBOL(disable_lapic_nmi_watchdog); EXPORT_SYMBOL(reserve_lapic_nmi);
EXPORT_SYMBOL(enable_lapic_nmi_watchdog); EXPORT_SYMBOL(release_lapic_nmi);
EXPORT_SYMBOL(disable_timer_nmi_watchdog); EXPORT_SYMBOL(disable_timer_nmi_watchdog);
EXPORT_SYMBOL(enable_timer_nmi_watchdog); EXPORT_SYMBOL(enable_timer_nmi_watchdog);
...@@ -183,7 +183,10 @@ static int nmi_setup(void) ...@@ -183,7 +183,10 @@ static int nmi_setup(void)
* without actually triggering any NMIs as this will * without actually triggering any NMIs as this will
* break the core code horrifically. * break the core code horrifically.
*/ */
disable_lapic_nmi_watchdog(); if (reserve_lapic_nmi() < 0) {
free_msrs();
return -EBUSY;
}
/* We need to serialize save and setup for HT because the subset /* We need to serialize save and setup for HT because the subset
* of msrs are distinct for save and setup operations * of msrs are distinct for save and setup operations
*/ */
...@@ -241,7 +244,7 @@ static void nmi_shutdown(void) ...@@ -241,7 +244,7 @@ static void nmi_shutdown(void)
nmi_enabled = 0; nmi_enabled = 0;
on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1); on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1);
unset_nmi_callback(); unset_nmi_callback();
enable_lapic_nmi_watchdog(); release_lapic_nmi();
free_msrs(); free_msrs();
} }
......
...@@ -33,6 +33,20 @@ ...@@ -33,6 +33,20 @@
#include <asm/proto.h> #include <asm/proto.h>
#include <asm/kdebug.h> #include <asm/kdebug.h>
/*
* lapic_nmi_owner tracks the ownership of the lapic NMI hardware:
* - it may be reserved by some other driver, or not
* - when not reserved by some other driver, it may be used for
* the NMI watchdog, or not
*
* This is maintained separately from nmi_active because the NMI
* watchdog may also be driven from the I/O APIC timer.
*/
static spinlock_t lapic_nmi_owner_lock = SPIN_LOCK_UNLOCKED;
static unsigned int lapic_nmi_owner;
#define LAPIC_NMI_WATCHDOG (1<<0)
#define LAPIC_NMI_RESERVED (1<<1)
/* nmi_active: /* nmi_active:
* +1: the lapic NMI watchdog is active, but can be disabled * +1: the lapic NMI watchdog is active, but can be disabled
* 0: the lapic NMI watchdog has not been set up, and cannot * 0: the lapic NMI watchdog has not been set up, and cannot
...@@ -122,6 +136,7 @@ int __init check_nmi_watchdog (void) ...@@ -122,6 +136,7 @@ int __init check_nmi_watchdog (void)
cpu, cpu,
cpu_pda[cpu].__nmi_count); cpu_pda[cpu].__nmi_count);
nmi_active = 0; nmi_active = 0;
lapic_nmi_owner &= ~LAPIC_NMI_WATCHDOG;
return -1; return -1;
} }
} }
...@@ -157,7 +172,7 @@ int __init setup_nmi_watchdog(char *str) ...@@ -157,7 +172,7 @@ int __init setup_nmi_watchdog(char *str)
__setup("nmi_watchdog=", setup_nmi_watchdog); __setup("nmi_watchdog=", setup_nmi_watchdog);
void disable_lapic_nmi_watchdog(void) static void disable_lapic_nmi_watchdog(void)
{ {
if (nmi_active <= 0) if (nmi_active <= 0)
return; return;
...@@ -174,7 +189,7 @@ void disable_lapic_nmi_watchdog(void) ...@@ -174,7 +189,7 @@ void disable_lapic_nmi_watchdog(void)
nmi_watchdog = 0; nmi_watchdog = 0;
} }
void enable_lapic_nmi_watchdog(void) static void enable_lapic_nmi_watchdog(void)
{ {
if (nmi_active < 0) { if (nmi_active < 0) {
nmi_watchdog = NMI_LOCAL_APIC; nmi_watchdog = NMI_LOCAL_APIC;
...@@ -182,6 +197,33 @@ void enable_lapic_nmi_watchdog(void) ...@@ -182,6 +197,33 @@ void enable_lapic_nmi_watchdog(void)
} }
} }
int reserve_lapic_nmi(void)
{
unsigned int old_owner;
spin_lock(&lapic_nmi_owner_lock);
old_owner = lapic_nmi_owner;
lapic_nmi_owner |= LAPIC_NMI_RESERVED;
spin_unlock(&lapic_nmi_owner_lock);
if (old_owner & LAPIC_NMI_RESERVED)
return -EBUSY;
if (old_owner & LAPIC_NMI_WATCHDOG)
disable_lapic_nmi_watchdog();
return 0;
}
void release_lapic_nmi(void)
{
unsigned int new_owner;
spin_lock(&lapic_nmi_owner_lock);
new_owner = lapic_nmi_owner & ~LAPIC_NMI_RESERVED;
lapic_nmi_owner = new_owner;
spin_unlock(&lapic_nmi_owner_lock);
if (new_owner & LAPIC_NMI_WATCHDOG)
enable_lapic_nmi_watchdog();
}
void disable_timer_nmi_watchdog(void) void disable_timer_nmi_watchdog(void)
{ {
if ((nmi_watchdog != NMI_IO_APIC) || (nmi_active <= 0)) if ((nmi_watchdog != NMI_IO_APIC) || (nmi_active <= 0))
...@@ -236,7 +278,7 @@ static int __init init_lapic_nmi_sysfs(void) ...@@ -236,7 +278,7 @@ static int __init init_lapic_nmi_sysfs(void)
{ {
int error; int error;
if (nmi_active == 0) if (nmi_active == 0 || nmi_watchdog != NMI_LOCAL_APIC)
return 0; return 0;
error = sysdev_class_register(&nmi_sysclass); error = sysdev_class_register(&nmi_sysclass);
...@@ -298,6 +340,7 @@ void setup_apic_nmi_watchdog(void) ...@@ -298,6 +340,7 @@ void setup_apic_nmi_watchdog(void)
default: default:
return; return;
} }
lapic_nmi_owner = LAPIC_NMI_WATCHDOG;
nmi_active = 1; nmi_active = 1;
} }
...@@ -405,8 +448,8 @@ void unset_nmi_callback(void) ...@@ -405,8 +448,8 @@ void unset_nmi_callback(void)
EXPORT_SYMBOL(nmi_active); EXPORT_SYMBOL(nmi_active);
EXPORT_SYMBOL(nmi_watchdog); EXPORT_SYMBOL(nmi_watchdog);
EXPORT_SYMBOL(disable_lapic_nmi_watchdog); EXPORT_SYMBOL(reserve_lapic_nmi);
EXPORT_SYMBOL(enable_lapic_nmi_watchdog); EXPORT_SYMBOL(release_lapic_nmi);
EXPORT_SYMBOL(disable_timer_nmi_watchdog); EXPORT_SYMBOL(disable_timer_nmi_watchdog);
EXPORT_SYMBOL(enable_timer_nmi_watchdog); EXPORT_SYMBOL(enable_timer_nmi_watchdog);
EXPORT_SYMBOL(touch_nmi_watchdog); EXPORT_SYMBOL(touch_nmi_watchdog);
...@@ -81,8 +81,8 @@ extern void smp_local_timer_interrupt (struct pt_regs * regs); ...@@ -81,8 +81,8 @@ extern void smp_local_timer_interrupt (struct pt_regs * regs);
extern void setup_boot_APIC_clock (void); extern void setup_boot_APIC_clock (void);
extern void setup_secondary_APIC_clock (void); extern void setup_secondary_APIC_clock (void);
extern void setup_apic_nmi_watchdog (void); extern void setup_apic_nmi_watchdog (void);
extern void disable_lapic_nmi_watchdog(void); extern int reserve_lapic_nmi(void);
extern void enable_lapic_nmi_watchdog(void); extern void release_lapic_nmi(void);
extern void disable_timer_nmi_watchdog(void); extern void disable_timer_nmi_watchdog(void);
extern void enable_timer_nmi_watchdog(void); extern void enable_timer_nmi_watchdog(void);
extern void nmi_watchdog_tick (struct pt_regs * regs); extern void nmi_watchdog_tick (struct pt_regs * regs);
......
...@@ -75,8 +75,8 @@ extern void smp_local_timer_interrupt (struct pt_regs * regs); ...@@ -75,8 +75,8 @@ extern void smp_local_timer_interrupt (struct pt_regs * regs);
extern void setup_boot_APIC_clock (void); extern void setup_boot_APIC_clock (void);
extern void setup_secondary_APIC_clock (void); extern void setup_secondary_APIC_clock (void);
extern void setup_apic_nmi_watchdog (void); extern void setup_apic_nmi_watchdog (void);
extern void disable_lapic_nmi_watchdog(void); extern int reserve_lapic_nmi(void);
extern void enable_lapic_nmi_watchdog(void); extern void release_lapic_nmi(void);
extern void disable_timer_nmi_watchdog(void); extern void disable_timer_nmi_watchdog(void);
extern void enable_timer_nmi_watchdog(void); extern void enable_timer_nmi_watchdog(void);
extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment