Commit 5ec29e31 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'core-locking-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull core locking updates from Ingo Molnar:
 "This update:

   - extends and simplifies x86 NMI callback handling code to enhance
     and fix the HP hw-watchdog driver

   - simplifies the x86 NMI callback handling code to fix a kmemcheck
     bug.

   - enhances the hung-task debugger"

* 'core-locking-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/nmi: Fix the type of the nmiaction.flags field
  x86/nmi: Fix page faults by nmiaction if kmemcheck is enabled
  x86/nmi: Add new NMI queues to deal with IO_CHK and SERR
  watchdog, hpwdt: Remove priority option for NMI callback
  hung task debugging: Inject NMI when hung and going to panic
parents abd209b7 6ff968cc
...@@ -27,6 +27,8 @@ void arch_trigger_all_cpu_backtrace(void); ...@@ -27,6 +27,8 @@ void arch_trigger_all_cpu_backtrace(void);
enum { enum {
NMI_LOCAL=0, NMI_LOCAL=0,
NMI_UNKNOWN, NMI_UNKNOWN,
NMI_SERR,
NMI_IO_CHECK,
NMI_MAX NMI_MAX
}; };
...@@ -35,8 +37,24 @@ enum { ...@@ -35,8 +37,24 @@ enum {
typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *);
int register_nmi_handler(unsigned int, nmi_handler_t, unsigned long, struct nmiaction {
const char *); struct list_head list;
nmi_handler_t handler;
unsigned long flags;
const char *name;
};
#define register_nmi_handler(t, fn, fg, n) \
({ \
static struct nmiaction fn##_na = { \
.handler = (fn), \
.name = (n), \
.flags = (fg), \
}; \
__register_nmi_handler((t), &fn##_na); \
})
int __register_nmi_handler(unsigned int, struct nmiaction *);
void unregister_nmi_handler(unsigned int, const char *); void unregister_nmi_handler(unsigned int, const char *);
......
...@@ -31,14 +31,6 @@ ...@@ -31,14 +31,6 @@
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/x86_init.h> #include <asm/x86_init.h>
#define NMI_MAX_NAMELEN 16
struct nmiaction {
struct list_head list;
nmi_handler_t handler;
unsigned int flags;
char *name;
};
struct nmi_desc { struct nmi_desc {
spinlock_t lock; spinlock_t lock;
struct list_head head; struct list_head head;
...@@ -54,6 +46,14 @@ static struct nmi_desc nmi_desc[NMI_MAX] = ...@@ -54,6 +46,14 @@ static struct nmi_desc nmi_desc[NMI_MAX] =
.lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock), .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock),
.head = LIST_HEAD_INIT(nmi_desc[1].head), .head = LIST_HEAD_INIT(nmi_desc[1].head),
}, },
{
.lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[2].lock),
.head = LIST_HEAD_INIT(nmi_desc[2].head),
},
{
.lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[3].lock),
.head = LIST_HEAD_INIT(nmi_desc[3].head),
},
}; };
...@@ -107,11 +107,14 @@ static int notrace __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, ...@@ -107,11 +107,14 @@ static int notrace __kprobes nmi_handle(unsigned int type, struct pt_regs *regs,
return handled; return handled;
} }
static int __setup_nmi(unsigned int type, struct nmiaction *action) int __register_nmi_handler(unsigned int type, struct nmiaction *action)
{ {
struct nmi_desc *desc = nmi_to_desc(type); struct nmi_desc *desc = nmi_to_desc(type);
unsigned long flags; unsigned long flags;
if (!action->handler)
return -EINVAL;
spin_lock_irqsave(&desc->lock, flags); spin_lock_irqsave(&desc->lock, flags);
/* /*
...@@ -120,6 +123,8 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action) ...@@ -120,6 +123,8 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action)
* to manage expectations * to manage expectations
*/ */
WARN_ON_ONCE(type == NMI_UNKNOWN && !list_empty(&desc->head)); WARN_ON_ONCE(type == NMI_UNKNOWN && !list_empty(&desc->head));
WARN_ON_ONCE(type == NMI_SERR && !list_empty(&desc->head));
WARN_ON_ONCE(type == NMI_IO_CHECK && !list_empty(&desc->head));
/* /*
* some handlers need to be executed first otherwise a fake * some handlers need to be executed first otherwise a fake
...@@ -133,8 +138,9 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action) ...@@ -133,8 +138,9 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action)
spin_unlock_irqrestore(&desc->lock, flags); spin_unlock_irqrestore(&desc->lock, flags);
return 0; return 0;
} }
EXPORT_SYMBOL(__register_nmi_handler);
static struct nmiaction *__free_nmi(unsigned int type, const char *name) void unregister_nmi_handler(unsigned int type, const char *name)
{ {
struct nmi_desc *desc = nmi_to_desc(type); struct nmi_desc *desc = nmi_to_desc(type);
struct nmiaction *n; struct nmiaction *n;
...@@ -157,61 +163,16 @@ static struct nmiaction *__free_nmi(unsigned int type, const char *name) ...@@ -157,61 +163,16 @@ static struct nmiaction *__free_nmi(unsigned int type, const char *name)
spin_unlock_irqrestore(&desc->lock, flags); spin_unlock_irqrestore(&desc->lock, flags);
synchronize_rcu(); 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); EXPORT_SYMBOL_GPL(unregister_nmi_handler);
static notrace __kprobes void static notrace __kprobes void
pci_serr_error(unsigned char reason, struct pt_regs *regs) pci_serr_error(unsigned char reason, struct pt_regs *regs)
{ {
/* check to see if anyone registered against these types of errors */
if (nmi_handle(NMI_SERR, regs, false))
return;
pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n", pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n",
reason, smp_processor_id()); reason, smp_processor_id());
...@@ -241,6 +202,10 @@ io_check_error(unsigned char reason, struct pt_regs *regs) ...@@ -241,6 +202,10 @@ io_check_error(unsigned char reason, struct pt_regs *regs)
{ {
unsigned long i; unsigned long i;
/* check to see if anyone registered against these types of errors */
if (nmi_handle(NMI_IO_CHECK, regs, false))
return;
pr_emerg( pr_emerg(
"NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n", "NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n",
reason, smp_processor_id()); reason, smp_processor_id());
......
...@@ -147,7 +147,6 @@ struct cmn_registers { ...@@ -147,7 +147,6 @@ struct cmn_registers {
static unsigned int hpwdt_nmi_decoding; static unsigned int hpwdt_nmi_decoding;
static unsigned int allow_kdump; static unsigned int allow_kdump;
static unsigned int priority; /* hpwdt at end of die_notify list */
static unsigned int is_icru; static unsigned int is_icru;
static DEFINE_SPINLOCK(rom_lock); static DEFINE_SPINLOCK(rom_lock);
static void *cru_rom_addr; static void *cru_rom_addr;
...@@ -723,28 +722,35 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) ...@@ -723,28 +722,35 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
} }
/* /*
* If the priority is set to 1, then we will be put first on the * Only one function can register for NMI_UNKNOWN
* die notify list to handle a critical NMI. The default is to
* be last so other users of the NMI signal can function.
*/ */
retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt");
(priority) ? NMI_FLAG_FIRST : 0, if (retval)
"hpwdt"); goto error;
if (retval != 0) { retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt");
dev_warn(&dev->dev, if (retval)
"Unable to register a die notifier (err=%d).\n", goto error1;
retval); retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt");
if (cru_rom_addr) if (retval)
iounmap(cru_rom_addr); goto error2;
}
dev_info(&dev->dev, dev_info(&dev->dev,
"HP Watchdog Timer Driver: NMI decoding initialized" "HP Watchdog Timer Driver: NMI decoding initialized"
", allow kernel dump: %s (default = 0/OFF)" ", allow kernel dump: %s (default = 0/OFF)\n",
", priority: %s (default = 0/LAST).\n", (allow_kdump == 0) ? "OFF" : "ON");
(allow_kdump == 0) ? "OFF" : "ON",
(priority == 0) ? "LAST" : "FIRST");
return 0; return 0;
error2:
unregister_nmi_handler(NMI_SERR, "hpwdt");
error1:
unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
error:
dev_warn(&dev->dev,
"Unable to register a die notifier (err=%d).\n",
retval);
if (cru_rom_addr)
iounmap(cru_rom_addr);
return retval;
} }
static void hpwdt_exit_nmi_decoding(void) static void hpwdt_exit_nmi_decoding(void)
...@@ -881,10 +887,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" ...@@ -881,10 +887,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
#ifdef CONFIG_HPWDT_NMI_DECODING #ifdef CONFIG_HPWDT_NMI_DECODING
module_param(allow_kdump, int, 0); module_param(allow_kdump, int, 0);
MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
module_param(priority, int, 0);
MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last"
" (default = 0/Last)\n");
#endif /* !CONFIG_HPWDT_NMI_DECODING */ #endif /* !CONFIG_HPWDT_NMI_DECODING */
module_init(hpwdt_init); module_init(hpwdt_init);
......
...@@ -108,8 +108,10 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout) ...@@ -108,8 +108,10 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout)
touch_nmi_watchdog(); touch_nmi_watchdog();
if (sysctl_hung_task_panic) if (sysctl_hung_task_panic) {
trigger_all_cpu_backtrace();
panic("hung_task: blocked tasks"); panic("hung_task: blocked tasks");
}
} }
/* /*
......
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