Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+20 −2
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ void arch_trigger_all_cpu_backtrace(void);
enum {
	NMI_LOCAL=0,
	NMI_UNKNOWN,
	NMI_SERR,
	NMI_IO_CHECK,
	NMI_MAX
};

@@ -35,8 +37,24 @@ enum {

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

int register_nmi_handler(unsigned int, nmi_handler_t, unsigned long,
			 const char *);
struct nmiaction {
	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 *);

+24 −59
Original line number Diff line number Diff line
@@ -31,14 +31,6 @@
#include <asm/nmi.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 {
	spinlock_t lock;
	struct list_head head;
@@ -54,6 +46,14 @@ static struct nmi_desc nmi_desc[NMI_MAX] =
		.lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock),
		.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,
	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);
	unsigned long flags;

	if (!action->handler)
		return -EINVAL;

	spin_lock_irqsave(&desc->lock, flags);

	/*
@@ -120,6 +123,8 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action)
	 * to manage expectations
	 */
	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
@@ -133,8 +138,9 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action)
	spin_unlock_irqrestore(&desc->lock, flags);
	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 nmiaction *n;
@@ -157,61 +163,16 @@ static struct nmiaction *__free_nmi(unsigned int type, const char *name)

	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)
{
	/* 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",
		 reason, smp_processor_id());

@@ -241,6 +202,10 @@ io_check_error(unsigned char reason, struct pt_regs *regs)
{
	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(
	"NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n",
		 reason, smp_processor_id());
+24 −22
Original line number Diff line number Diff line
@@ -147,7 +147,6 @@ struct cmn_registers {

static unsigned int hpwdt_nmi_decoding;
static unsigned int allow_kdump;
static unsigned int priority;		/* hpwdt at end of die_notify list */
static unsigned int is_icru;
static DEFINE_SPINLOCK(rom_lock);
static void *cru_rom_addr;
@@ -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
	 * die notify list to handle a critical NMI. The default is to
	 * be last so other users of the NMI signal can function.
	 * Only one function can register for NMI_UNKNOWN
	 */
	retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout,
					(priority) ? NMI_FLAG_FIRST : 0,
					"hpwdt");
	if (retval != 0) {
	retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt");
	if (retval)
		goto error;
	retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt");
	if (retval)
		goto error1;
	retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt");
	if (retval)
		goto error2;

	dev_info(&dev->dev,
			"HP Watchdog Timer Driver: NMI decoding initialized"
			", allow kernel dump: %s (default = 0/OFF)\n",
			(allow_kdump == 0) ? "OFF" : "ON");
	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);
	}

	dev_info(&dev->dev,
			"HP Watchdog Timer Driver: NMI decoding initialized"
			", allow kernel dump: %s (default = 0/OFF)"
			", priority: %s (default = 0/LAST).\n",
			(allow_kdump == 0) ? "OFF" : "ON",
			(priority == 0) ? "LAST" : "FIRST");
	return 0;
	return retval;
}

static void hpwdt_exit_nmi_decoding(void)
@@ -881,10 +887,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
#ifdef CONFIG_HPWDT_NMI_DECODING
module_param(allow_kdump, int, 0);
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 */

module_init(hpwdt_init);
+3 −1
Original line number Diff line number Diff line
@@ -108,9 +108,11 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout)

	touch_nmi_watchdog();

	if (sysctl_hung_task_panic)
	if (sysctl_hung_task_panic) {
		trigger_all_cpu_backtrace();
		panic("hung_task: blocked tasks");
	}
}

/*
 * To avoid extending the RCU grace period for an unbounded amount of time,