Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 269034
b: refs/heads/master
c: c9126b2
h: refs/heads/master
v: v3
  • Loading branch information
Don Zickus authored and Ingo Molnar committed Oct 10, 2011
1 parent 77d5f65 commit aa2738b
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 1d48922c14b6363f6d5febb12464d804bb5cc53f
refs/heads/master: c9126b2ee8adb9235941cedbf558d39a9e65642d
18 changes: 18 additions & 0 deletions trunk/arch/x86/include/asm/nmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ void arch_trigger_all_cpu_backtrace(void);
#define NMI_LOCAL_NORMAL_PRIOR (NMI_LOCAL_BIT | NMI_NORMAL_PRIOR)
#define NMI_LOCAL_LOW_PRIOR (NMI_LOCAL_BIT | NMI_LOW_PRIOR)

#define NMI_FLAG_FIRST 1

enum {
NMI_LOCAL=0,
NMI_UNKNOWN,
NMI_MAX
};

#define NMI_DONE 0
#define NMI_HANDLED 1

typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *);

int register_nmi_handler(unsigned int, nmi_handler_t, unsigned long,
const char *);

void unregister_nmi_handler(unsigned int, const char *);

void stop_nmi(void);
void restart_nmi(void);

Expand Down
153 changes: 153 additions & 0 deletions trunk/arch/x86/kernel/nmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#include <linux/kprobes.h>
#include <linux/kdebug.h>
#include <linux/nmi.h>
#include <linux/delay.h>
#include <linux/hardirq.h>
#include <linux/slab.h>

#if defined(CONFIG_EDAC)
#include <linux/edac.h>
Expand All @@ -21,6 +24,33 @@
#include <linux/atomic.h>
#include <asm/traps.h>
#include <asm/mach_traps.h>
#include <asm/nmi.h>

#define NMI_MAX_NAMELEN 16
struct nmiaction {
struct list_head list;
nmi_handler_t handler;
unsigned int flags;
char *name;
};

struct nmi_desc {
spinlock_t lock;
struct list_head head;
};

static struct nmi_desc nmi_desc[NMI_MAX] =
{
{
.lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[0].lock),
.head = LIST_HEAD_INIT(nmi_desc[0].head),
},
{
.lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock),
.head = LIST_HEAD_INIT(nmi_desc[1].head),
},

};

static int ignore_nmis;

Expand All @@ -38,6 +68,129 @@ static int __init setup_unknown_nmi_panic(char *str)
}
__setup("unknown_nmi_panic", setup_unknown_nmi_panic);

#define nmi_to_desc(type) (&nmi_desc[type])

static int notrace __kprobes nmi_handle(unsigned int type, struct pt_regs *regs)
{
struct nmi_desc *desc = nmi_to_desc(type);
struct nmiaction *a;
int handled=0;

rcu_read_lock();

/*
* NMIs are edge-triggered, which means if you have enough
* of them concurrently, you can lose some because only one
* can be latched at any given time. Walk the whole list
* to handle those situations.
*/
list_for_each_entry_rcu(a, &desc->head, list) {

handled += a->handler(type, regs);

}

rcu_read_unlock();

/* return total number of NMI events handled */
return handled;
}

static int __setup_nmi(unsigned int type, struct nmiaction *action)
{
struct nmi_desc *desc = nmi_to_desc(type);
unsigned long flags;

spin_lock_irqsave(&desc->lock, flags);

/*
* some handlers need to be executed first otherwise a fake
* event confuses some handlers (kdump uses this flag)
*/
if (action->flags & NMI_FLAG_FIRST)
list_add_rcu(&action->list, &desc->head);
else
list_add_tail_rcu(&action->list, &desc->head);

spin_unlock_irqrestore(&desc->lock, flags);
return 0;
}

static struct nmiaction *__free_nmi(unsigned int type, const char *name)
{
struct nmi_desc *desc = nmi_to_desc(type);
struct nmiaction *n;
unsigned long flags;

spin_lock_irqsave(&desc->lock, flags);

list_for_each_entry_rcu(n, &desc->head, list) {
/*
* the name passed in to describe the nmi handler
* is used as the lookup key
*/
if (!strcmp(n->name, name)) {
WARN(in_nmi(),
"Trying to free NMI (%s) from NMI context!\n", n->name);
list_del_rcu(&n->list);
break;
}
}

spin_unlock_irqrestore(&desc->lock, flags);
synchronize_rcu();
return (n);
}

int register_nmi_handler(unsigned int type, nmi_handler_t handler,
unsigned long nmiflags, const char *devname)
{
struct nmiaction *action;
int retval = -ENOMEM;

if (!handler)
return -EINVAL;

action = kzalloc(sizeof(struct nmiaction), GFP_KERNEL);
if (!action)
goto fail_action;

action->handler = handler;
action->flags = nmiflags;
action->name = kstrndup(devname, NMI_MAX_NAMELEN, GFP_KERNEL);
if (!action->name)
goto fail_action_name;

retval = __setup_nmi(type, action);

if (retval)
goto fail_setup_nmi;

return retval;

fail_setup_nmi:
kfree(action->name);
fail_action_name:
kfree(action);
fail_action:

return retval;
}
EXPORT_SYMBOL_GPL(register_nmi_handler);

void unregister_nmi_handler(unsigned int type, const char *name)
{
struct nmiaction *a;

a = __free_nmi(type, name);
if (a) {
kfree(a->name);
kfree(a);
}
}

EXPORT_SYMBOL_GPL(unregister_nmi_handler);

static notrace __kprobes void
pci_serr_error(unsigned char reason, struct pt_regs *regs)
{
Expand Down

0 comments on commit aa2738b

Please sign in to comment.