mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
[PATCH] i386/x86-64: Remove un/set_nmi_callback and reserve/release_lapic_nmi functions
Removes the un/set_nmi_callback and reserve/release_lapic_nmi functions as they are no longer needed. The various subsystems are modified to register with the die_notifier instead. Also includes compile fixes by Andrew Morton. Signed-off-by: Don Zickus <dzickus@redhat.com> Signed-off-by: Andi Kleen <ak@suse.de>
This commit is contained in:
parent
957dc87c1b
commit
2fbe7b25c8
10 changed files with 116 additions and 260 deletions
|
@ -22,6 +22,8 @@
|
||||||
#include <asm/nmi.h>
|
#include <asm/nmi.h>
|
||||||
#include <asm/hw_irq.h>
|
#include <asm/hw_irq.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
#include <asm/kdebug.h>
|
||||||
|
|
||||||
#include <mach_ipi.h>
|
#include <mach_ipi.h>
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,9 +95,18 @@ static void crash_save_self(struct pt_regs *regs)
|
||||||
#if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC)
|
#if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC)
|
||||||
static atomic_t waiting_for_crash_ipi;
|
static atomic_t waiting_for_crash_ipi;
|
||||||
|
|
||||||
static int crash_nmi_callback(struct pt_regs *regs, int cpu)
|
static int crash_nmi_callback(struct notifier_block *self,
|
||||||
|
unsigned long val, void *data)
|
||||||
{
|
{
|
||||||
|
struct pt_regs *regs;
|
||||||
struct pt_regs fixed_regs;
|
struct pt_regs fixed_regs;
|
||||||
|
int cpu;
|
||||||
|
|
||||||
|
if (val != DIE_NMI)
|
||||||
|
return NOTIFY_OK;
|
||||||
|
|
||||||
|
regs = ((struct die_args *)data)->regs;
|
||||||
|
cpu = raw_smp_processor_id();
|
||||||
|
|
||||||
/* Don't do anything if this handler is invoked on crashing cpu.
|
/* Don't do anything if this handler is invoked on crashing cpu.
|
||||||
* Otherwise, system will completely hang. Crashing cpu can get
|
* Otherwise, system will completely hang. Crashing cpu can get
|
||||||
|
@ -125,13 +136,18 @@ static void smp_send_nmi_allbutself(void)
|
||||||
send_IPI_allbutself(NMI_VECTOR);
|
send_IPI_allbutself(NMI_VECTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct notifier_block crash_nmi_nb = {
|
||||||
|
.notifier_call = crash_nmi_callback,
|
||||||
|
};
|
||||||
|
|
||||||
static void nmi_shootdown_cpus(void)
|
static void nmi_shootdown_cpus(void)
|
||||||
{
|
{
|
||||||
unsigned long msecs;
|
unsigned long msecs;
|
||||||
|
|
||||||
atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
|
atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
|
||||||
/* Would it be better to replace the trap vector here? */
|
/* Would it be better to replace the trap vector here? */
|
||||||
set_nmi_callback(crash_nmi_callback);
|
if (register_die_notifier(&crash_nmi_nb))
|
||||||
|
return; /* return what? */
|
||||||
/* Ensure the new callback function is set before sending
|
/* Ensure the new callback function is set before sending
|
||||||
* out the NMI
|
* out the NMI
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -42,20 +42,6 @@ static DEFINE_PER_CPU(unsigned long, evntsel_nmi_owner[3]);
|
||||||
*/
|
*/
|
||||||
#define NMI_MAX_COUNTER_BITS 66
|
#define NMI_MAX_COUNTER_BITS 66
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 DEFINE_SPINLOCK(lapic_nmi_owner_lock);
|
|
||||||
static unsigned int lapic_nmi_owner;
|
|
||||||
#define LAPIC_NMI_WATCHDOG (1<<0)
|
|
||||||
#define LAPIC_NMI_RESERVED (1<<1)
|
|
||||||
|
|
||||||
/* nmi_active:
|
/* nmi_active:
|
||||||
* >0: the lapic NMI watchdog is active, but can be disabled
|
* >0: 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
|
||||||
|
@ -325,33 +311,6 @@ static void enable_lapic_nmi_watchdog(void)
|
||||||
touch_nmi_watchdog();
|
touch_nmi_watchdog();
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
BUG_ON(nmi_watchdog != NMI_IO_APIC);
|
BUG_ON(nmi_watchdog != NMI_IO_APIC);
|
||||||
|
@ -866,6 +825,15 @@ int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_nmi_callback(struct pt_regs * regs, int cpu)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SYSCTL
|
||||||
|
if (unknown_nmi_panic)
|
||||||
|
return unknown_nmi_panic_callback(regs, cpu);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SYSCTL
|
#ifdef CONFIG_SYSCTL
|
||||||
|
|
||||||
static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
|
static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
|
||||||
|
@ -873,37 +841,8 @@ static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
|
||||||
unsigned char reason = get_nmi_reason();
|
unsigned char reason = get_nmi_reason();
|
||||||
char buf[64];
|
char buf[64];
|
||||||
|
|
||||||
if (!(reason & 0xc0)) {
|
|
||||||
sprintf(buf, "NMI received for unknown reason %02x\n", reason);
|
sprintf(buf, "NMI received for unknown reason %02x\n", reason);
|
||||||
die_nmi(regs, buf);
|
die_nmi(regs, buf);
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* proc handler for /proc/sys/kernel/unknown_nmi_panic
|
|
||||||
*/
|
|
||||||
int proc_unknown_nmi_panic(ctl_table *table, int write, struct file *file,
|
|
||||||
void __user *buffer, size_t *length, loff_t *ppos)
|
|
||||||
{
|
|
||||||
int old_state;
|
|
||||||
|
|
||||||
old_state = unknown_nmi_panic;
|
|
||||||
proc_dointvec(table, write, file, buffer, length, ppos);
|
|
||||||
if (!!old_state == !!unknown_nmi_panic)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (unknown_nmi_panic) {
|
|
||||||
if (reserve_lapic_nmi() < 0) {
|
|
||||||
unknown_nmi_panic = 0;
|
|
||||||
return -EBUSY;
|
|
||||||
} else {
|
|
||||||
set_nmi_callback(unknown_nmi_panic_callback);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
release_lapic_nmi();
|
|
||||||
unset_nmi_callback();
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -917,7 +856,5 @@ EXPORT_SYMBOL(reserve_perfctr_nmi);
|
||||||
EXPORT_SYMBOL(release_perfctr_nmi);
|
EXPORT_SYMBOL(release_perfctr_nmi);
|
||||||
EXPORT_SYMBOL(reserve_evntsel_nmi);
|
EXPORT_SYMBOL(reserve_evntsel_nmi);
|
||||||
EXPORT_SYMBOL(release_evntsel_nmi);
|
EXPORT_SYMBOL(release_evntsel_nmi);
|
||||||
EXPORT_SYMBOL(reserve_lapic_nmi);
|
|
||||||
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);
|
||||||
|
|
|
@ -706,13 +706,6 @@ void die_nmi (struct pt_regs *regs, const char *msg)
|
||||||
do_exit(SIGSEGV);
|
do_exit(SIGSEGV);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static nmi_callback_t nmi_callback = dummy_nmi_callback;
|
|
||||||
|
|
||||||
static void default_do_nmi(struct pt_regs * regs)
|
static void default_do_nmi(struct pt_regs * regs)
|
||||||
{
|
{
|
||||||
unsigned char reason = 0;
|
unsigned char reason = 0;
|
||||||
|
@ -732,9 +725,10 @@ static void default_do_nmi(struct pt_regs * regs)
|
||||||
*/
|
*/
|
||||||
if (nmi_watchdog_tick(regs, reason))
|
if (nmi_watchdog_tick(regs, reason))
|
||||||
return;
|
return;
|
||||||
|
if (!do_nmi_callback(regs, smp_processor_id()))
|
||||||
#endif
|
#endif
|
||||||
if (!rcu_dereference(nmi_callback)(regs, smp_processor_id()))
|
|
||||||
unknown_nmi_error(reason, regs);
|
unknown_nmi_error(reason, regs);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
|
if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
|
||||||
|
@ -765,19 +759,6 @@ fastcall void do_nmi(struct pt_regs * regs, long error_code)
|
||||||
nmi_exit();
|
nmi_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_nmi_callback(nmi_callback_t callback)
|
|
||||||
{
|
|
||||||
vmalloc_sync_all();
|
|
||||||
rcu_assign_pointer(nmi_callback, callback);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(set_nmi_callback);
|
|
||||||
|
|
||||||
void unset_nmi_callback(void)
|
|
||||||
{
|
|
||||||
nmi_callback = dummy_nmi_callback;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(unset_nmi_callback);
|
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code)
|
fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code)
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <asm/nmi.h>
|
#include <asm/nmi.h>
|
||||||
#include <asm/msr.h>
|
#include <asm/msr.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
#include <asm/kdebug.h>
|
||||||
|
|
||||||
#include "op_counter.h"
|
#include "op_counter.h"
|
||||||
#include "op_x86_model.h"
|
#include "op_x86_model.h"
|
||||||
|
@ -82,12 +83,23 @@ static void exit_driverfs(void)
|
||||||
#define exit_driverfs() do { } while (0)
|
#define exit_driverfs() do { } while (0)
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
int profile_exceptions_notify(struct notifier_block *self,
|
||||||
static int nmi_callback(struct pt_regs * regs, int cpu)
|
unsigned long val, void *data)
|
||||||
{
|
{
|
||||||
return model->check_ctrs(regs, &cpu_msrs[cpu]);
|
struct die_args *args = (struct die_args *)data;
|
||||||
}
|
int ret = NOTIFY_DONE;
|
||||||
|
int cpu = smp_processor_id();
|
||||||
|
|
||||||
|
switch(val) {
|
||||||
|
case DIE_NMI:
|
||||||
|
if (model->check_ctrs(args->regs, &cpu_msrs[cpu]))
|
||||||
|
ret = NOTIFY_STOP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void nmi_cpu_save_registers(struct op_msrs * msrs)
|
static void nmi_cpu_save_registers(struct op_msrs * msrs)
|
||||||
{
|
{
|
||||||
|
@ -174,27 +186,29 @@ static void nmi_cpu_setup(void * dummy)
|
||||||
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct notifier_block profile_exceptions_nb = {
|
||||||
|
.notifier_call = profile_exceptions_notify,
|
||||||
|
.next = NULL,
|
||||||
|
.priority = 0
|
||||||
|
};
|
||||||
|
|
||||||
static int nmi_setup(void)
|
static int nmi_setup(void)
|
||||||
{
|
{
|
||||||
|
int err=0;
|
||||||
|
|
||||||
if (!allocate_msrs())
|
if (!allocate_msrs())
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* We walk a thin line between law and rape here.
|
if ((err = register_die_notifier(&profile_exceptions_nb))){
|
||||||
* We need to be careful to install our NMI handler
|
|
||||||
* without actually triggering any NMIs as this will
|
|
||||||
* break the core code horrifically.
|
|
||||||
*/
|
|
||||||
if (reserve_lapic_nmi() < 0) {
|
|
||||||
free_msrs();
|
free_msrs();
|
||||||
return -EBUSY;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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
|
||||||
*/
|
*/
|
||||||
on_each_cpu(nmi_save_registers, NULL, 0, 1);
|
on_each_cpu(nmi_save_registers, NULL, 0, 1);
|
||||||
on_each_cpu(nmi_cpu_setup, NULL, 0, 1);
|
on_each_cpu(nmi_cpu_setup, NULL, 0, 1);
|
||||||
set_nmi_callback(nmi_callback);
|
|
||||||
nmi_enabled = 1;
|
nmi_enabled = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -250,8 +264,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();
|
unregister_die_notifier(&profile_exceptions_nb);
|
||||||
release_lapic_nmi();
|
|
||||||
free_msrs();
|
free_msrs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,32 +17,49 @@
|
||||||
#include <asm/nmi.h>
|
#include <asm/nmi.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
|
#include <asm/kdebug.h>
|
||||||
|
|
||||||
static int nmi_timer_callback(struct pt_regs * regs, int cpu)
|
int profile_timer_exceptions_notify(struct notifier_block *self,
|
||||||
|
unsigned long val, void *data)
|
||||||
{
|
{
|
||||||
oprofile_add_sample(regs, 0);
|
struct die_args *args = (struct die_args *)data;
|
||||||
return 1;
|
int ret = NOTIFY_DONE;
|
||||||
|
|
||||||
|
switch(val) {
|
||||||
|
case DIE_NMI:
|
||||||
|
oprofile_add_sample(args->regs, 0);
|
||||||
|
ret = NOTIFY_STOP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct notifier_block profile_timer_exceptions_nb = {
|
||||||
|
.notifier_call = profile_timer_exceptions_notify,
|
||||||
|
.next = NULL,
|
||||||
|
.priority = 0
|
||||||
|
};
|
||||||
|
|
||||||
static int timer_start(void)
|
static int timer_start(void)
|
||||||
{
|
{
|
||||||
disable_timer_nmi_watchdog();
|
if (register_die_notifier(&profile_timer_exceptions_nb))
|
||||||
set_nmi_callback(nmi_timer_callback);
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void timer_stop(void)
|
static void timer_stop(void)
|
||||||
{
|
{
|
||||||
enable_timer_nmi_watchdog();
|
unregister_die_notifier(&profile_timer_exceptions_nb);
|
||||||
unset_nmi_callback();
|
|
||||||
synchronize_sched(); /* Allow already-started NMIs to complete. */
|
synchronize_sched(); /* Allow already-started NMIs to complete. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int __init op_nmi_timer_init(struct oprofile_operations * ops)
|
int __init op_nmi_timer_init(struct oprofile_operations * ops)
|
||||||
{
|
{
|
||||||
if (atomic_read(&nmi_active) <= 0)
|
if ((nmi_watchdog != NMI_IO_APIC) || (atomic_read(&nmi_active) <= 0))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
ops->start = timer_start;
|
ops->start = timer_start;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <asm/nmi.h>
|
#include <asm/nmi.h>
|
||||||
#include <asm/hw_irq.h>
|
#include <asm/hw_irq.h>
|
||||||
#include <asm/mach_apic.h>
|
#include <asm/mach_apic.h>
|
||||||
|
#include <asm/kdebug.h>
|
||||||
|
|
||||||
/* This keeps a track of which one is crashing cpu. */
|
/* This keeps a track of which one is crashing cpu. */
|
||||||
static int crashing_cpu;
|
static int crashing_cpu;
|
||||||
|
@ -95,8 +96,18 @@ static void crash_save_self(struct pt_regs *regs)
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
static atomic_t waiting_for_crash_ipi;
|
static atomic_t waiting_for_crash_ipi;
|
||||||
|
|
||||||
static int crash_nmi_callback(struct pt_regs *regs, int cpu)
|
static int crash_nmi_callback(struct notifier_block *self,
|
||||||
|
unsigned long val, void *data)
|
||||||
{
|
{
|
||||||
|
struct pt_regs *regs;
|
||||||
|
int cpu;
|
||||||
|
|
||||||
|
if (val != DIE_NMI)
|
||||||
|
return NOTIFY_OK;
|
||||||
|
|
||||||
|
regs = ((struct die_args *)data)->regs;
|
||||||
|
cpu = raw_smp_processor_id();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't do anything if this handler is invoked on crashing cpu.
|
* Don't do anything if this handler is invoked on crashing cpu.
|
||||||
* Otherwise, system will completely hang. Crashing cpu can get
|
* Otherwise, system will completely hang. Crashing cpu can get
|
||||||
|
@ -127,12 +138,17 @@ static void smp_send_nmi_allbutself(void)
|
||||||
* cpu hotplug shouldn't matter.
|
* cpu hotplug shouldn't matter.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static struct notifier_block crash_nmi_nb = {
|
||||||
|
.notifier_call = crash_nmi_callback,
|
||||||
|
};
|
||||||
|
|
||||||
static void nmi_shootdown_cpus(void)
|
static void nmi_shootdown_cpus(void)
|
||||||
{
|
{
|
||||||
unsigned long msecs;
|
unsigned long msecs;
|
||||||
|
|
||||||
atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
|
atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
|
||||||
set_nmi_callback(crash_nmi_callback);
|
if (register_die_notifier(&crash_nmi_nb))
|
||||||
|
return; /* return what? */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ensure the new callback function is set before sending
|
* Ensure the new callback function is set before sending
|
||||||
|
|
|
@ -41,20 +41,6 @@ static DEFINE_PER_CPU(unsigned, evntsel_nmi_owner[2]);
|
||||||
*/
|
*/
|
||||||
#define NMI_MAX_COUNTER_BITS 66
|
#define NMI_MAX_COUNTER_BITS 66
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 DEFINE_SPINLOCK(lapic_nmi_owner_lock);
|
|
||||||
static unsigned int lapic_nmi_owner;
|
|
||||||
#define LAPIC_NMI_WATCHDOG (1<<0)
|
|
||||||
#define LAPIC_NMI_RESERVED (1<<1)
|
|
||||||
|
|
||||||
/* nmi_active:
|
/* nmi_active:
|
||||||
* >0: the lapic NMI watchdog is active, but can be disabled
|
* >0: 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
|
||||||
|
@ -321,33 +307,6 @@ static void enable_lapic_nmi_watchdog(void)
|
||||||
touch_nmi_watchdog();
|
touch_nmi_watchdog();
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
BUG_ON(nmi_watchdog != NMI_IO_APIC);
|
BUG_ON(nmi_watchdog != NMI_IO_APIC);
|
||||||
|
@ -762,13 +721,6 @@ int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __kprobes int dummy_nmi_callback(struct pt_regs * regs, int cpu)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static nmi_callback_t nmi_callback = dummy_nmi_callback;
|
|
||||||
|
|
||||||
asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
|
asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
|
||||||
{
|
{
|
||||||
nmi_enter();
|
nmi_enter();
|
||||||
|
@ -779,22 +731,13 @@ asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
|
||||||
|
|
||||||
int do_nmi_callback(struct pt_regs * regs, int cpu)
|
int do_nmi_callback(struct pt_regs * regs, int cpu)
|
||||||
{
|
{
|
||||||
return rcu_dereference(nmi_callback)(regs, cpu);
|
#ifdef CONFIG_SYSCTL
|
||||||
|
if (unknown_nmi_panic)
|
||||||
|
return unknown_nmi_panic_callback(regs, cpu);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_nmi_callback(nmi_callback_t callback)
|
|
||||||
{
|
|
||||||
vmalloc_sync_all();
|
|
||||||
rcu_assign_pointer(nmi_callback, callback);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(set_nmi_callback);
|
|
||||||
|
|
||||||
void unset_nmi_callback(void)
|
|
||||||
{
|
|
||||||
nmi_callback = dummy_nmi_callback;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(unset_nmi_callback);
|
|
||||||
|
|
||||||
#ifdef CONFIG_SYSCTL
|
#ifdef CONFIG_SYSCTL
|
||||||
|
|
||||||
static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
|
static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
|
||||||
|
@ -802,37 +745,8 @@ static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
|
||||||
unsigned char reason = get_nmi_reason();
|
unsigned char reason = get_nmi_reason();
|
||||||
char buf[64];
|
char buf[64];
|
||||||
|
|
||||||
if (!(reason & 0xc0)) {
|
|
||||||
sprintf(buf, "NMI received for unknown reason %02x\n", reason);
|
sprintf(buf, "NMI received for unknown reason %02x\n", reason);
|
||||||
die_nmi(buf,regs);
|
die_nmi(buf,regs);
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* proc handler for /proc/sys/kernel/unknown_nmi_panic
|
|
||||||
*/
|
|
||||||
int proc_unknown_nmi_panic(struct ctl_table *table, int write, struct file *file,
|
|
||||||
void __user *buffer, size_t *length, loff_t *ppos)
|
|
||||||
{
|
|
||||||
int old_state;
|
|
||||||
|
|
||||||
old_state = unknown_nmi_panic;
|
|
||||||
proc_dointvec(table, write, file, buffer, length, ppos);
|
|
||||||
if (!!old_state == !!unknown_nmi_panic)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (unknown_nmi_panic) {
|
|
||||||
if (reserve_lapic_nmi() < 0) {
|
|
||||||
unknown_nmi_panic = 0;
|
|
||||||
return -EBUSY;
|
|
||||||
} else {
|
|
||||||
set_nmi_callback(unknown_nmi_panic_callback);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
release_lapic_nmi();
|
|
||||||
unset_nmi_callback();
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,8 +760,6 @@ EXPORT_SYMBOL(reserve_perfctr_nmi);
|
||||||
EXPORT_SYMBOL(release_perfctr_nmi);
|
EXPORT_SYMBOL(release_perfctr_nmi);
|
||||||
EXPORT_SYMBOL(reserve_evntsel_nmi);
|
EXPORT_SYMBOL(reserve_evntsel_nmi);
|
||||||
EXPORT_SYMBOL(release_evntsel_nmi);
|
EXPORT_SYMBOL(release_evntsel_nmi);
|
||||||
EXPORT_SYMBOL(reserve_lapic_nmi);
|
|
||||||
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);
|
||||||
|
|
|
@ -6,24 +6,13 @@
|
||||||
|
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
|
|
||||||
struct pt_regs;
|
|
||||||
|
|
||||||
typedef int (*nmi_callback_t)(struct pt_regs * regs, int cpu);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set_nmi_callback
|
* do_nmi_callback
|
||||||
*
|
*
|
||||||
* Set a handler for an NMI. Only one handler may be
|
* Check to see if a callback exists and execute it. Return 1
|
||||||
* set. Return 1 if the NMI was handled.
|
* if the handler exists and was handled successfully.
|
||||||
*/
|
*/
|
||||||
void set_nmi_callback(nmi_callback_t callback);
|
int do_nmi_callback(struct pt_regs *regs, int cpu);
|
||||||
|
|
||||||
/**
|
|
||||||
* unset_nmi_callback
|
|
||||||
*
|
|
||||||
* Remove the handler previously set.
|
|
||||||
*/
|
|
||||||
void unset_nmi_callback(void);
|
|
||||||
|
|
||||||
extern int avail_to_resrv_perfctr_nmi_bit(unsigned int);
|
extern int avail_to_resrv_perfctr_nmi_bit(unsigned int);
|
||||||
extern int avail_to_resrv_perfctr_nmi(unsigned int);
|
extern int avail_to_resrv_perfctr_nmi(unsigned int);
|
||||||
|
@ -33,8 +22,6 @@ extern int reserve_evntsel_nmi(unsigned int);
|
||||||
extern void release_evntsel_nmi(unsigned int);
|
extern void release_evntsel_nmi(unsigned int);
|
||||||
|
|
||||||
extern void setup_apic_nmi_watchdog (void *);
|
extern void setup_apic_nmi_watchdog (void *);
|
||||||
extern int reserve_lapic_nmi(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 int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
|
extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
|
||||||
|
|
|
@ -7,25 +7,6 @@
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
struct pt_regs;
|
|
||||||
|
|
||||||
typedef int (*nmi_callback_t)(struct pt_regs * regs, int cpu);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set_nmi_callback
|
|
||||||
*
|
|
||||||
* Set a handler for an NMI. Only one handler may be
|
|
||||||
* set. Return 1 if the NMI was handled.
|
|
||||||
*/
|
|
||||||
void set_nmi_callback(nmi_callback_t callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* unset_nmi_callback
|
|
||||||
*
|
|
||||||
* Remove the handler previously set.
|
|
||||||
*/
|
|
||||||
void unset_nmi_callback(void);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do_nmi_callback
|
* do_nmi_callback
|
||||||
*
|
*
|
||||||
|
@ -72,8 +53,6 @@ extern int reserve_evntsel_nmi(unsigned int);
|
||||||
extern void release_evntsel_nmi(unsigned int);
|
extern void release_evntsel_nmi(unsigned int);
|
||||||
|
|
||||||
extern void setup_apic_nmi_watchdog (void *);
|
extern void setup_apic_nmi_watchdog (void *);
|
||||||
extern int reserve_lapic_nmi(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 int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
|
extern int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
|
||||||
|
|
|
@ -76,8 +76,6 @@ extern int compat_log;
|
||||||
|
|
||||||
#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86)
|
#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86)
|
||||||
int unknown_nmi_panic;
|
int unknown_nmi_panic;
|
||||||
extern int proc_unknown_nmi_panic(ctl_table *, int, struct file *,
|
|
||||||
void __user *, size_t *, loff_t *);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
|
/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
|
||||||
|
@ -628,7 +626,7 @@ static ctl_table kern_table[] = {
|
||||||
.data = &unknown_nmi_panic,
|
.data = &unknown_nmi_panic,
|
||||||
.maxlen = sizeof (int),
|
.maxlen = sizeof (int),
|
||||||
.mode = 0644,
|
.mode = 0644,
|
||||||
.proc_handler = &proc_unknown_nmi_panic,
|
.proc_handler = &proc_dointvec,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_X86)
|
#if defined(CONFIG_X86)
|
||||||
|
|
Loading…
Reference in a new issue