linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/4] Provide core API for NMIs
@ 2019-01-28 15:38 Julien Thierry
  2019-01-28 15:38 ` [PATCH v5 1/4] genirq: Provide basic NMI management for interrupt lines Julien Thierry
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Julien Thierry @ 2019-01-28 15:38 UTC (permalink / raw)
  To: linux-kernel; +Cc: tglx, peterz, marc.zyngier, mingo, Julien Thierry

Hi,

This patch series provides a way for irqchips to define some IRQs as NMIs.

Updating this series as it is needed for the arm64 pseudo-NMI[1] which we
are considering to merge.

Changes since v4[2]:
- Rebased on v5.0-rc4
- Modified comment for request_percpu_nmi() to detail
  the use of per CPU NMIs a bit more

Changes since v3[3]:
- Rebased on v4.20-rc2

Changes since v2[4]:
- Fix ARM build error reported by kbuild bot
- Fix documentation warnings reported by kbuild bot

Changes since v1[5]:
- Rebased to v4.19-rc1
- Fix some coding style issues

Changes since RFC[6]:
- Rebased to v4.18-rc6
- Remove disable_nmi, one should call disable_nmi_nosync instead
- Remove code related to affinity balancing from NMI functions
- Require PERCPU configuration for NMIs

[1] https://lkml.org/lkml/2019/1/21/1060
[2] https://lkml.org/lkml/2018/11/12/2113
[3] https://lkml.org/lkml/2018/8/31/173
[4] https://lkml.org/lkml/2018/8/28/661
[5] https://lkml.org/lkml/2018/7/24/321
[6] https://lkml.org/lkml/2018/7/9/768

Cheers,

Julien

-->

Julien Thierry (4):
  genirq: Provide basic NMI management for interrupt lines
  genirq: Provide NMI management for percpu_devid interrupts
  genirq: Provide NMI handlers
  irqdesc: Add domain handler for NMIs

 include/linux/interrupt.h |  18 +++
 include/linux/irq.h       |  10 ++
 include/linux/irqdesc.h   |   5 +
 kernel/irq/chip.c         |  54 +++++++
 kernel/irq/debugfs.c      |   6 +-
 kernel/irq/internals.h    |   2 +
 kernel/irq/irqdesc.c      |  35 +++++
 kernel/irq/manage.c       | 376 +++++++++++++++++++++++++++++++++++++++++++++-
 8 files changed, 503 insertions(+), 3 deletions(-)

--
1.9.1

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v5 1/4] genirq: Provide basic NMI management for interrupt lines
  2019-01-28 15:38 [PATCH v5 0/4] Provide core API for NMIs Julien Thierry
@ 2019-01-28 15:38 ` Julien Thierry
  2019-01-28 16:25   ` Marc Zyngier
  2019-01-28 15:38 ` [PATCH v5 2/4] genirq: Provide NMI management for percpu_devid interrupts Julien Thierry
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Julien Thierry @ 2019-01-28 15:38 UTC (permalink / raw)
  To: linux-kernel; +Cc: tglx, peterz, marc.zyngier, mingo, Julien Thierry

Add functionality to allocate interrupt lines that will deliver IRQs
as Non-Maskable Interrupts. These allocations are only successful if
the irqchip provides the necessary support and allows NMI delivery for the
interrupt line.

Interrupt lines allocated for NMI delivery must be enabled/disabled through
enable_nmi/disable_nmi_nosync to keep their state consistent.

To treat a PERCPU IRQ as NMI, the interrupt must not be shared nor threaded,
the irqchip directly managing the IRQ must be the root irqchip and the
irqchip cannot be behind a slow bus.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
---
 include/linux/interrupt.h |   9 ++
 include/linux/irq.h       |   7 ++
 kernel/irq/debugfs.c      |   6 +-
 kernel/irq/internals.h    |   2 +
 kernel/irq/manage.c       | 228 +++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 249 insertions(+), 3 deletions(-)

diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 4a728db..37a4b0c6 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -156,6 +156,10 @@ struct irqaction {
 		     unsigned long flags, const char *devname,
 		     void __percpu *percpu_dev_id);
 
+extern int __must_check
+request_nmi(unsigned int irq, irq_handler_t handler, unsigned long flags,
+	    const char *name, void *dev);
+
 static inline int __must_check
 request_percpu_irq(unsigned int irq, irq_handler_t handler,
 		   const char *devname, void __percpu *percpu_dev_id)
@@ -167,6 +171,8 @@ struct irqaction {
 extern const void *free_irq(unsigned int, void *);
 extern void free_percpu_irq(unsigned int, void __percpu *);
 
+extern const void *free_nmi(unsigned int irq, void *dev_id);
+
 struct device;
 
 extern int __must_check
@@ -217,6 +223,9 @@ struct irqaction {
 extern bool irq_percpu_is_enabled(unsigned int irq);
 extern void irq_wake_thread(unsigned int irq, void *dev_id);
 
+extern void disable_nmi_nosync(unsigned int irq);
+extern void enable_nmi(unsigned int irq);
+
 /* The following three functions are for the core kernel use only. */
 extern void suspend_device_irqs(void);
 extern void resume_device_irqs(void);
diff --git a/include/linux/irq.h b/include/linux/irq.h
index def2b2a..a7298e4 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -442,6 +442,8 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
  * @irq_set_vcpu_affinity:	optional to target a vCPU in a virtual machine
  * @ipi_send_single:	send a single IPI to destination cpus
  * @ipi_send_mask:	send an IPI to destination cpus in cpumask
+ * @irq_nmi_setup:	function called from core code before enabling an NMI
+ * @irq_nmi_teardown:	function called from core code after disabling an NMI
  * @flags:		chip specific flags
  */
 struct irq_chip {
@@ -490,6 +492,9 @@ struct irq_chip {
 	void		(*ipi_send_single)(struct irq_data *data, unsigned int cpu);
 	void		(*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
 
+	int		(*irq_nmi_setup)(struct irq_data *data);
+	void		(*irq_nmi_teardown)(struct irq_data *data);
+
 	unsigned long	flags;
 };
 
@@ -505,6 +510,7 @@ struct irq_chip {
  * IRQCHIP_ONESHOT_SAFE:	One shot does not require mask/unmask
  * IRQCHIP_EOI_THREADED:	Chip requires eoi() on unmask in threaded mode
  * IRQCHIP_SUPPORTS_LEVEL_MSI	Chip can provide two doorbells for Level MSIs
+ * IRQCHIP_SUPPORTS_NMI:	Chip can deliver NMIs, only for root irqchips
  */
 enum {
 	IRQCHIP_SET_TYPE_MASKED		= (1 <<  0),
@@ -515,6 +521,7 @@ enum {
 	IRQCHIP_ONESHOT_SAFE		= (1 <<  5),
 	IRQCHIP_EOI_THREADED		= (1 <<  6),
 	IRQCHIP_SUPPORTS_LEVEL_MSI	= (1 <<  7),
+	IRQCHIP_SUPPORTS_NMI		= (1 <<  8),
 };
 
 #include <linux/irqdesc.h>
diff --git a/kernel/irq/debugfs.c b/kernel/irq/debugfs.c
index 6f63613..59a04d2 100644
--- a/kernel/irq/debugfs.c
+++ b/kernel/irq/debugfs.c
@@ -56,6 +56,7 @@ static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc) { }
 	BIT_MASK_DESCR(IRQCHIP_ONESHOT_SAFE),
 	BIT_MASK_DESCR(IRQCHIP_EOI_THREADED),
 	BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI),
+	BIT_MASK_DESCR(IRQCHIP_SUPPORTS_NMI),
 };
 
 static void
@@ -140,6 +141,7 @@ static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc) { }
 	BIT_MASK_DESCR(IRQS_WAITING),
 	BIT_MASK_DESCR(IRQS_PENDING),
 	BIT_MASK_DESCR(IRQS_SUSPENDED),
+	BIT_MASK_DESCR(IRQS_NMI),
 };
 
 
@@ -203,8 +205,8 @@ static ssize_t irq_debug_write(struct file *file, const char __user *user_buf,
 		chip_bus_lock(desc);
 		raw_spin_lock_irqsave(&desc->lock, flags);
 
-		if (irq_settings_is_level(desc)) {
-			/* Can't do level, sorry */
+		if (irq_settings_is_level(desc) || desc->istate & IRQS_NMI) {
+			/* Can't do level nor NMIs, sorry */
 			err = -EINVAL;
 		} else {
 			desc->istate |= IRQS_PENDING;
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index ca6afa2..2a77cdd2 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -49,6 +49,7 @@ enum {
  * IRQS_WAITING			- irq is waiting
  * IRQS_PENDING			- irq is pending and replayed later
  * IRQS_SUSPENDED		- irq is suspended
+ * IRQS_NMI			- irq line is used to deliver NMIs
  */
 enum {
 	IRQS_AUTODETECT		= 0x00000001,
@@ -60,6 +61,7 @@ enum {
 	IRQS_PENDING		= 0x00000200,
 	IRQS_SUSPENDED		= 0x00000800,
 	IRQS_TIMINGS		= 0x00001000,
+	IRQS_NMI		= 0x00002000,
 };
 
 #include "debug.h"
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 84b54a1..a168b2d8f 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -341,7 +341,7 @@ static void irq_affinity_notify(struct work_struct *work)
 	/* The release function is promised process context */
 	might_sleep();
 
-	if (!desc)
+	if (!desc || desc->istate & IRQS_NMI)
 		return -EINVAL;
 
 	/* Complete initialisation of *notify */
@@ -553,6 +553,21 @@ bool disable_hardirq(unsigned int irq)
 }
 EXPORT_SYMBOL_GPL(disable_hardirq);
 
+/**
+ *	disable_nmi_nosync - disable an nmi without waiting
+ *	@irq: Interrupt to disable
+ *
+ *	Disable the selected interrupt line. Disables and enables are
+ *	nested.
+ *	The interrupt to disable must have been requested through request_nmi.
+ *	Unlike disable_nmi(), this function does not ensure existing
+ *	instances of the IRQ handler have completed before returning.
+ */
+void disable_nmi_nosync(unsigned int irq)
+{
+	disable_irq_nosync(irq);
+}
+
 void __enable_irq(struct irq_desc *desc)
 {
 	switch (desc->depth) {
@@ -609,6 +624,20 @@ void enable_irq(unsigned int irq)
 }
 EXPORT_SYMBOL(enable_irq);
 
+/**
+ *	enable_nmi - enable handling of an nmi
+ *	@irq: Interrupt to enable
+ *
+ *	The interrupt to enable must have been requested through request_nmi.
+ *	Undoes the effect of one call to disable_nmi(). If this
+ *	matches the last disable, processing of interrupts on this
+ *	IRQ line is re-enabled.
+ */
+void enable_nmi(unsigned int irq)
+{
+	enable_irq(irq);
+}
+
 static int set_irq_wake_real(unsigned int irq, unsigned int on)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
@@ -644,6 +673,12 @@ int irq_set_irq_wake(unsigned int irq, unsigned int on)
 	if (!desc)
 		return -EINVAL;
 
+	/* Don't use NMIs as wake up interrupts please */
+	if (desc->istate & IRQS_NMI) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
 	/* wakeup-capable irqs can be shared between drivers that
 	 * don't need to have the same sleep mode behaviors.
 	 */
@@ -666,6 +701,8 @@ int irq_set_irq_wake(unsigned int irq, unsigned int on)
 				irqd_clear(&desc->irq_data, IRQD_WAKEUP_STATE);
 		}
 	}
+
+out_unlock:
 	irq_put_desc_busunlock(desc, flags);
 	return ret;
 }
@@ -1128,6 +1165,39 @@ static void irq_release_resources(struct irq_desc *desc)
 		c->irq_release_resources(d);
 }
 
+static bool irq_supports_nmi(struct irq_desc *desc)
+{
+	struct irq_data *d = irq_desc_get_irq_data(desc);
+
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+	/* Only IRQs directly managed by the root irqchip can be set as NMI */
+	if (d->parent_data)
+		return false;
+#endif
+	/* Don't support NMIs for chips behind a slow bus */
+	if (d->chip->irq_bus_lock || d->chip->irq_bus_sync_unlock)
+		return false;
+
+	return d->chip->flags & IRQCHIP_SUPPORTS_NMI;
+}
+
+static int irq_nmi_setup(struct irq_desc *desc)
+{
+	struct irq_data *d = irq_desc_get_irq_data(desc);
+	struct irq_chip *c = d->chip;
+
+	return c->irq_nmi_setup ? c->irq_nmi_setup(d) : -EINVAL;
+}
+
+static void irq_nmi_teardown(struct irq_desc *desc)
+{
+	struct irq_data *d = irq_desc_get_irq_data(desc);
+	struct irq_chip *c = d->chip;
+
+	if (c->irq_nmi_teardown)
+		c->irq_nmi_teardown(d);
+}
+
 static int
 setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary)
 {
@@ -1302,9 +1372,17 @@ static void irq_release_resources(struct irq_desc *desc)
 		 * fields must have IRQF_SHARED set and the bits which
 		 * set the trigger type must match. Also all must
 		 * agree on ONESHOT.
+		 * Interrupt lines used for NMIs cannot be shared.
 		 */
 		unsigned int oldtype;
 
+		if (desc->istate & IRQS_NMI) {
+			pr_err("Invalid attempt to share NMI for %s (irq %d) on irqchip %s.\n",
+				new->name, irq, desc->irq_data.chip->name);
+			ret = -EINVAL;
+			goto out_unlock;
+		}
+
 		/*
 		 * If nobody did set the configuration before, inherit
 		 * the one provided by the requester.
@@ -1756,6 +1834,59 @@ const void *free_irq(unsigned int irq, void *dev_id)
 }
 EXPORT_SYMBOL(free_irq);
 
+/* This function must be called with desc->lock held */
+static const void *__cleanup_nmi(unsigned int irq, struct irq_desc *desc)
+{
+	const char *devname = NULL;
+
+	desc->istate &= ~IRQS_NMI;
+
+	if (!WARN_ON(desc->action == NULL)) {
+		irq_pm_remove_action(desc, desc->action);
+		devname = desc->action->name;
+		unregister_handler_proc(irq, desc->action);
+
+		kfree(desc->action);
+		desc->action = NULL;
+	}
+
+	irq_settings_clr_disable_unlazy(desc);
+	irq_shutdown(desc);
+
+	irq_release_resources(desc);
+
+	irq_chip_pm_put(&desc->irq_data);
+	module_put(desc->owner);
+
+	return devname;
+}
+
+const void *free_nmi(unsigned int irq, void *dev_id)
+{
+	struct irq_desc *desc = irq_to_desc(irq);
+	unsigned long flags;
+	const void *devname;
+
+	if (!desc || WARN_ON(!(desc->istate & IRQS_NMI)))
+		return NULL;
+
+	if (WARN_ON(irq_settings_is_per_cpu_devid(desc)))
+		return NULL;
+
+	/* NMI still enabled */
+	if (WARN_ON(desc->depth == 0))
+		disable_nmi_nosync(irq);
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+
+	irq_nmi_teardown(desc);
+	devname = __cleanup_nmi(irq, desc);
+
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	return devname;
+}
+
 /**
  *	request_threaded_irq - allocate an interrupt line
  *	@irq: Interrupt line to allocate
@@ -1925,6 +2056,101 @@ int request_any_context_irq(unsigned int irq, irq_handler_t handler,
 }
 EXPORT_SYMBOL_GPL(request_any_context_irq);
 
+/**
+ *	request_nmi - allocate an interrupt line for NMI delivery
+ *	@irq: Interrupt line to allocate
+ *	@handler: Function to be called when the IRQ occurs.
+ *		  Threaded handler for threaded interrupts.
+ *	@irqflags: Interrupt type flags
+ *	@name: An ascii name for the claiming device
+ *	@dev_id: A cookie passed back to the handler function
+ *
+ *	This call allocates interrupt resources and enables the
+ *	interrupt line and IRQ handling. It sets up the IRQ line
+ *	to be handled as an NMI.
+ *
+ *	An interrupt line delivering NMIs cannot be shared and IRQ handling
+ *	cannot be threaded.
+ *
+ *	Interrupt lines requested for NMI delivering must produce per cpu
+ *	interrupts and have auto enabling setting disabled.
+ *
+ *	Dev_id must be globally unique. Normally the address of the
+ *	device data structure is used as the cookie. Since the handler
+ *	receives this value it makes sense to use it.
+ *
+ *	If the interrupt line cannot be used to deliver NMIs, function
+ *	will fail and return a negative value.
+ */
+int request_nmi(unsigned int irq, irq_handler_t handler,
+		unsigned long irqflags, const char *name, void *dev_id)
+{
+	struct irqaction *action;
+	struct irq_desc *desc;
+	unsigned long flags;
+	int retval;
+
+	if (irq == IRQ_NOTCONNECTED)
+		return -ENOTCONN;
+
+	/* NMI cannot be shared, used for Polling */
+	if (irqflags & (IRQF_SHARED | IRQF_COND_SUSPEND | IRQF_IRQPOLL))
+		return -EINVAL;
+
+	if (!(irqflags & IRQF_PERCPU))
+		return -EINVAL;
+
+	if (!handler)
+		return -EINVAL;
+
+	desc = irq_to_desc(irq);
+
+	if (!desc || irq_settings_can_autoenable(desc) ||
+	    !irq_settings_can_request(desc) ||
+	    WARN_ON(irq_settings_is_per_cpu_devid(desc)) ||
+	    !irq_supports_nmi(desc))
+		return -EINVAL;
+
+	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
+	if (!action)
+		return -ENOMEM;
+
+	action->handler = handler;
+	action->flags = irqflags | IRQF_NO_THREAD | IRQF_NOBALANCING;
+	action->name = name;
+	action->dev_id = dev_id;
+
+	retval = irq_chip_pm_get(&desc->irq_data);
+	if (retval < 0)
+		goto err_out;
+
+	retval = __setup_irq(irq, desc, action);
+	if (retval)
+		goto err_irq_setup;
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+
+	/* Setup NMI state */
+	desc->istate |= IRQS_NMI;
+	retval = irq_nmi_setup(desc);
+	if (retval) {
+		__cleanup_nmi(irq, desc);
+		raw_spin_unlock_irqrestore(&desc->lock, flags);
+		return -EINVAL;
+	}
+
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	return 0;
+
+err_irq_setup:
+	irq_chip_pm_put(&desc->irq_data);
+err_out:
+	kfree(action);
+
+	return retval;
+}
+
 void enable_percpu_irq(unsigned int irq, unsigned int type)
 {
 	unsigned int cpu = smp_processor_id();
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v5 2/4] genirq: Provide NMI management for percpu_devid interrupts
  2019-01-28 15:38 [PATCH v5 0/4] Provide core API for NMIs Julien Thierry
  2019-01-28 15:38 ` [PATCH v5 1/4] genirq: Provide basic NMI management for interrupt lines Julien Thierry
@ 2019-01-28 15:38 ` Julien Thierry
  2019-01-28 16:41   ` Marc Zyngier
  2019-01-28 15:38 ` [PATCH v5 3/4] genirq: Provide NMI handlers Julien Thierry
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Julien Thierry @ 2019-01-28 15:38 UTC (permalink / raw)
  To: linux-kernel; +Cc: tglx, peterz, marc.zyngier, mingo, Julien Thierry

Add support for percpu_devid interrupts treated as NMIs.

Percpu_devid NMIs need to be setup/torn down on each CPU they target.

The same restrictions as for global NMIs still apply for percpu_devid NMIs.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
---
 include/linux/interrupt.h |   9 +++
 kernel/irq/manage.c       | 148 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+)

diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 37a4b0c6..be146bd 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -168,10 +168,15 @@ struct irqaction {
 				    devname, percpu_dev_id);
 }
 
+extern int __must_check
+request_percpu_nmi(unsigned int irq, irq_handler_t handler,
+		   const char *devname, void __percpu *dev);
+
 extern const void *free_irq(unsigned int, void *);
 extern void free_percpu_irq(unsigned int, void __percpu *);
 
 extern const void *free_nmi(unsigned int irq, void *dev_id);
+extern void free_percpu_nmi(unsigned int irq, void __percpu *percpu_dev_id);
 
 struct device;
 
@@ -224,7 +229,11 @@ struct irqaction {
 extern void irq_wake_thread(unsigned int irq, void *dev_id);
 
 extern void disable_nmi_nosync(unsigned int irq);
+extern void disable_percpu_nmi(unsigned int irq);
 extern void enable_nmi(unsigned int irq);
+extern void enable_percpu_nmi(unsigned int irq, unsigned int type);
+extern int ready_percpu_nmi(unsigned int irq);
+extern void teardown_percpu_nmi(unsigned int irq);
 
 /* The following three functions are for the core kernel use only. */
 extern void suspend_device_irqs(void);
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index a168b2d8f..602a622 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -2185,6 +2185,11 @@ void enable_percpu_irq(unsigned int irq, unsigned int type)
 }
 EXPORT_SYMBOL_GPL(enable_percpu_irq);
 
+void enable_percpu_nmi(unsigned int irq, unsigned int type)
+{
+	enable_percpu_irq(irq, type);
+}
+
 /**
  * irq_percpu_is_enabled - Check whether the per cpu irq is enabled
  * @irq:	Linux irq number to check for
@@ -2224,6 +2229,11 @@ void disable_percpu_irq(unsigned int irq)
 }
 EXPORT_SYMBOL_GPL(disable_percpu_irq);
 
+void disable_percpu_nmi(unsigned int irq)
+{
+	disable_percpu_irq(irq);
+}
+
 /*
  * Internal function to unregister a percpu irqaction.
  */
@@ -2255,6 +2265,8 @@ static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_
 	/* Found it - now remove it from the list of entries: */
 	desc->action = NULL;
 
+	desc->istate &= ~IRQS_NMI;
+
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
 
 	unregister_handler_proc(irq, action);
@@ -2308,6 +2320,19 @@ void free_percpu_irq(unsigned int irq, void __percpu *dev_id)
 }
 EXPORT_SYMBOL_GPL(free_percpu_irq);
 
+void free_percpu_nmi(unsigned int irq, void __percpu *dev_id)
+{
+	struct irq_desc *desc = irq_to_desc(irq);
+
+	if (!desc || !irq_settings_is_per_cpu_devid(desc))
+		return;
+
+	if (WARN_ON(!(desc->istate & IRQS_NMI)))
+		return;
+
+	kfree(__free_percpu_irq(irq, dev_id));
+}
+
 /**
  *	setup_percpu_irq - setup a per-cpu interrupt
  *	@irq: Interrupt line to setup
@@ -2398,6 +2423,129 @@ int __request_percpu_irq(unsigned int irq, irq_handler_t handler,
 EXPORT_SYMBOL_GPL(__request_percpu_irq);
 
 /**
+ *	request_percpu_nmi - allocate a percpu interrupt line for NMI delivery
+ *	@irq: Interrupt line to allocate
+ *	@handler: Function to be called when the IRQ occurs.
+ *	@name: An ascii name for the claiming device
+ *	@dev_id: A percpu cookie passed back to the handler function
+ *
+ *	This call allocates interrupt resources for a per CPU NMI. Per CPU NMIs
+ *	have to be setup on each CPU by calling ready_percpu_nmi() before being
+ *	enabled on the same CPU by using enable_percpu_nmi().
+ *
+ *	Dev_id must be globally unique. It is a per-cpu variable, and
+ *	the handler gets called with the interrupted CPU's instance of
+ *	that variable.
+ *
+ *	Interrupt lines requested for NMI delivering should have auto enabling
+ *	setting disabled.
+ *
+ *	If the interrupt line cannot be used to deliver NMIs, function
+ *	will fail returning a negative value.
+ */
+int request_percpu_nmi(unsigned int irq, irq_handler_t handler,
+		       const char *name, void __percpu *dev_id)
+{
+	struct irqaction *action;
+	struct irq_desc *desc;
+	unsigned long flags;
+	int retval;
+
+	if (!handler)
+		return -EINVAL;
+
+	desc = irq_to_desc(irq);
+
+	if (!desc || !irq_settings_can_request(desc) ||
+	    !irq_settings_is_per_cpu_devid(desc) ||
+	    irq_settings_can_autoenable(desc) ||
+	    !irq_supports_nmi(desc))
+		return -EINVAL;
+
+	/* The line cannot already be NMI */
+	if (desc->istate & IRQS_NMI)
+		return -EINVAL;
+
+	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
+	if (!action)
+		return -ENOMEM;
+
+	action->handler = handler;
+	action->flags = IRQF_PERCPU | IRQF_NO_SUSPEND | IRQF_NO_THREAD
+		| IRQF_NOBALANCING;
+	action->name = name;
+	action->percpu_dev_id = dev_id;
+
+	retval = irq_chip_pm_get(&desc->irq_data);
+	if (retval < 0)
+		goto err_out;
+
+	retval = __setup_irq(irq, desc, action);
+	if (retval)
+		goto err_irq_setup;
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+	desc->istate |= IRQS_NMI;
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	return 0;
+
+err_irq_setup:
+	irq_chip_pm_put(&desc->irq_data);
+err_out:
+	kfree(action);
+
+	return retval;
+}
+
+int ready_percpu_nmi(unsigned int irq)
+{
+	unsigned long flags;
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags,
+						  IRQ_GET_DESC_CHECK_PERCPU);
+	int ret = 0;
+
+	if (!desc) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (WARN(!(desc->istate & IRQS_NMI),
+		 KERN_ERR "ready_percpu_nmi called for a non-NMI interrupt: irq %u\n",
+		 irq)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = irq_nmi_setup(desc);
+	if (ret) {
+		pr_err("Failed to setup NMI delivery: irq %u\n", irq);
+		goto out;
+	}
+
+out:
+	irq_put_desc_unlock(desc, flags);
+	return ret;
+}
+
+void teardown_percpu_nmi(unsigned int irq)
+{
+	unsigned long flags;
+	struct irq_desc *desc = irq_get_desc_lock(irq, &flags,
+						  IRQ_GET_DESC_CHECK_PERCPU);
+
+	if (!desc)
+		return;
+
+	if (WARN_ON(!(desc->istate & IRQS_NMI)))
+		goto out;
+
+	irq_nmi_teardown(desc);
+out:
+	irq_put_desc_unlock(desc, flags);
+}
+
+/**
  *	irq_get_irqchip_state - returns the irqchip state of a interrupt.
  *	@irq: Interrupt line that is forwarded to a VM
  *	@which: One of IRQCHIP_STATE_* the caller wants to know about
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v5 3/4] genirq: Provide NMI handlers
  2019-01-28 15:38 [PATCH v5 0/4] Provide core API for NMIs Julien Thierry
  2019-01-28 15:38 ` [PATCH v5 1/4] genirq: Provide basic NMI management for interrupt lines Julien Thierry
  2019-01-28 15:38 ` [PATCH v5 2/4] genirq: Provide NMI management for percpu_devid interrupts Julien Thierry
@ 2019-01-28 15:38 ` Julien Thierry
  2019-01-28 16:42   ` Marc Zyngier
  2019-01-28 15:38 ` [PATCH v5 4/4] irqdesc: Add domain handler for NMIs Julien Thierry
  2019-01-28 16:54 ` [PATCH v5 0/4] Provide core API " Marc Zyngier
  4 siblings, 1 reply; 11+ messages in thread
From: Julien Thierry @ 2019-01-28 15:38 UTC (permalink / raw)
  To: linux-kernel; +Cc: tglx, peterz, marc.zyngier, mingo, Julien Thierry

Provide flow handlers that are NMI safe for interrupts and percpu_devid
interrupts.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
---
 include/linux/irq.h |  3 +++
 kernel/irq/chip.c   | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)

diff --git a/include/linux/irq.h b/include/linux/irq.h
index a7298e4..5e91f6b 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -601,6 +601,9 @@ static inline int irq_set_parent(int irq, int parent_irq)
 extern void handle_bad_irq(struct irq_desc *desc);
 extern void handle_nested_irq(unsigned int irq);
 
+extern void handle_fasteoi_nmi(struct irq_desc *desc);
+extern void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc);
+
 extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
 extern int irq_chip_pm_get(struct irq_data *data);
 extern int irq_chip_pm_put(struct irq_data *data);
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 34e9690..c32d5f3 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -730,6 +730,37 @@ void handle_fasteoi_irq(struct irq_desc *desc)
 EXPORT_SYMBOL_GPL(handle_fasteoi_irq);
 
 /**
+ *	handle_fasteoi_nmi - irq handler for NMI interrupt lines
+ *	@desc:	the interrupt description structure for this irq
+ *
+ *	A simple NMI-safe handler, considering the restrictions
+ *	from request_nmi.
+ *
+ *	Only a single callback will be issued to the chip: an ->eoi()
+ *	call when the interrupt has been serviced. This enables support
+ *	for modern forms of interrupt handlers, which handle the flow
+ *	details in hardware, transparently.
+ */
+void handle_fasteoi_nmi(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct irqaction *action = desc->action;
+	unsigned int irq = irq_desc_get_irq(desc);
+	irqreturn_t res;
+
+	trace_irq_handler_entry(irq, action);
+	/*
+	 * NMIs cannot be shared, there is only one action.
+	 */
+	res = action->handler(irq, action->dev_id);
+	trace_irq_handler_exit(irq, action, res);
+
+	if (chip->irq_eoi)
+		chip->irq_eoi(&desc->irq_data);
+}
+EXPORT_SYMBOL_GPL(handle_fasteoi_nmi);
+
+/**
  *	handle_edge_irq - edge type IRQ handler
  *	@desc:	the interrupt description structure for this irq
  *
@@ -908,6 +939,29 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
 		chip->irq_eoi(&desc->irq_data);
 }
 
+/**
+ * handle_percpu_devid_fasteoi_nmi - Per CPU local NMI handler with per cpu
+ *				     dev ids
+ * @desc:	the interrupt description structure for this irq
+ *
+ * Similar to handle_fasteoi_nmi, but handling the dev_id cookie
+ * as a percpu pointer.
+ */
+void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct irqaction *action = desc->action;
+	unsigned int irq = irq_desc_get_irq(desc);
+	irqreturn_t res;
+
+	trace_irq_handler_entry(irq, action);
+	res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
+	trace_irq_handler_exit(irq, action, res);
+
+	if (chip->irq_eoi)
+		chip->irq_eoi(&desc->irq_data);
+}
+
 static void
 __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
 		     int is_chained, const char *name)
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v5 4/4] irqdesc: Add domain handler for NMIs
  2019-01-28 15:38 [PATCH v5 0/4] Provide core API for NMIs Julien Thierry
                   ` (2 preceding siblings ...)
  2019-01-28 15:38 ` [PATCH v5 3/4] genirq: Provide NMI handlers Julien Thierry
@ 2019-01-28 15:38 ` Julien Thierry
  2019-01-28 16:46   ` Marc Zyngier
  2019-01-28 16:54 ` [PATCH v5 0/4] Provide core API " Marc Zyngier
  4 siblings, 1 reply; 11+ messages in thread
From: Julien Thierry @ 2019-01-28 15:38 UTC (permalink / raw)
  To: linux-kernel
  Cc: tglx, peterz, marc.zyngier, mingo, Julien Thierry, Will Deacon

NMI handling code should be executed between calls to nmi_enter and
nmi_exit.

Add a separate domain handler to properly setup NMI context when handling
an interrupt requested as NMI.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
---
 include/linux/irqdesc.h |  5 +++++
 kernel/irq/irqdesc.c    | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)

diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index dd1e40d..ba05b0d 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -171,6 +171,11 @@ static inline int handle_domain_irq(struct irq_domain *domain,
 {
 	return __handle_domain_irq(domain, hwirq, true, regs);
 }
+
+#ifdef CONFIG_IRQ_DOMAIN
+int handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq,
+		      struct pt_regs *regs);
+#endif
 #endif
 
 /* Test to see if a driver has successfully requested an irq */
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index ef8ad36..6b08b2b 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -669,6 +669,41 @@ int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
 	set_irq_regs(old_regs);
 	return ret;
 }
+
+#ifdef CONFIG_IRQ_DOMAIN
+/**
+ * handle_domain_nmi - Invoke the handler for a HW irq belonging to a domain
+ * @domain:	The domain where to perform the lookup
+ * @hwirq:	The HW irq number to convert to a logical one
+ * @regs:	Register file coming from the low-level handling code
+ *
+ * Returns:	0 on success, or -EINVAL if conversion has failed
+ */
+int handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq,
+		      struct pt_regs *regs)
+{
+	struct pt_regs *old_regs = set_irq_regs(regs);
+	unsigned int irq;
+	int ret = 0;
+
+	nmi_enter();
+
+	irq = irq_find_mapping(domain, hwirq);
+
+	/*
+	 * ack_bad_irq is not NMI-safe, just report
+	 * an invalid interrupt.
+	 */
+	if (likely(irq))
+		generic_handle_irq(irq);
+	else
+		ret = -EINVAL;
+
+	nmi_exit();
+	set_irq_regs(old_regs);
+	return ret;
+}
+#endif
 #endif
 
 /* Dynamic interrupt handling */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 1/4] genirq: Provide basic NMI management for interrupt lines
  2019-01-28 15:38 ` [PATCH v5 1/4] genirq: Provide basic NMI management for interrupt lines Julien Thierry
@ 2019-01-28 16:25   ` Marc Zyngier
  0 siblings, 0 replies; 11+ messages in thread
From: Marc Zyngier @ 2019-01-28 16:25 UTC (permalink / raw)
  To: Julien Thierry; +Cc: linux-kernel, tglx, peterz, mingo

On Mon, 28 Jan 2019 15:38:24 +0000,
Julien Thierry <julien.thierry@arm.com> wrote:
> 
> Add functionality to allocate interrupt lines that will deliver IRQs
> as Non-Maskable Interrupts. These allocations are only successful if
> the irqchip provides the necessary support and allows NMI delivery for the
> interrupt line.
> 
> Interrupt lines allocated for NMI delivery must be enabled/disabled through
> enable_nmi/disable_nmi_nosync to keep their state consistent.
> 
> To treat a PERCPU IRQ as NMI, the interrupt must not be shared nor threaded,
> the irqchip directly managing the IRQ must be the root irqchip and the
> irqchip cannot be behind a slow bus.
> 
> Signed-off-by: Julien Thierry <julien.thierry@arm.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: Marc Zyngier <marc.zyngier@arm.com>

Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>

	M.

-- 
Jazz is not dead, it just smell funny.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 2/4] genirq: Provide NMI management for percpu_devid interrupts
  2019-01-28 15:38 ` [PATCH v5 2/4] genirq: Provide NMI management for percpu_devid interrupts Julien Thierry
@ 2019-01-28 16:41   ` Marc Zyngier
  2019-01-28 17:27     ` Julien Thierry
  0 siblings, 1 reply; 11+ messages in thread
From: Marc Zyngier @ 2019-01-28 16:41 UTC (permalink / raw)
  To: Julien Thierry; +Cc: linux-kernel, tglx, peterz, mingo

On Mon, 28 Jan 2019 15:38:25 +0000,
Julien Thierry <julien.thierry@arm.com> wrote:
> 
> Add support for percpu_devid interrupts treated as NMIs.
> 
> Percpu_devid NMIs need to be setup/torn down on each CPU they target.
> 
> The same restrictions as for global NMIs still apply for percpu_devid NMIs.

A quick overall view of the new API would be good, specially as some
of the bits are not 100% obvious.

> 
> Signed-off-by: Julien Thierry <julien.thierry@arm.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  include/linux/interrupt.h |   9 +++
>  kernel/irq/manage.c       | 148 ++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 157 insertions(+)
> 
> diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
> index 37a4b0c6..be146bd 100644
> --- a/include/linux/interrupt.h
> +++ b/include/linux/interrupt.h
> @@ -168,10 +168,15 @@ struct irqaction {
>  				    devname, percpu_dev_id);
>  }
>  
> +extern int __must_check
> +request_percpu_nmi(unsigned int irq, irq_handler_t handler,
> +		   const char *devname, void __percpu *dev);
> +
>  extern const void *free_irq(unsigned int, void *);
>  extern void free_percpu_irq(unsigned int, void __percpu *);
>  
>  extern const void *free_nmi(unsigned int irq, void *dev_id);
> +extern void free_percpu_nmi(unsigned int irq, void __percpu *percpu_dev_id);
>  
>  struct device;
>  
> @@ -224,7 +229,11 @@ struct irqaction {
>  extern void irq_wake_thread(unsigned int irq, void *dev_id);
>  
>  extern void disable_nmi_nosync(unsigned int irq);
> +extern void disable_percpu_nmi(unsigned int irq);
>  extern void enable_nmi(unsigned int irq);
> +extern void enable_percpu_nmi(unsigned int irq, unsigned int type);
> +extern int ready_percpu_nmi(unsigned int irq);

ready_percpu_nmi seems a very bizarre name, as I cannot figure out
what that does. How about something along the line of
"setup_percpu_nmi"?

> +extern void teardown_percpu_nmi(unsigned int irq);
>  
>  /* The following three functions are for the core kernel use only. */
>  extern void suspend_device_irqs(void);
> diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
> index a168b2d8f..602a622 100644
> --- a/kernel/irq/manage.c
> +++ b/kernel/irq/manage.c
> @@ -2185,6 +2185,11 @@ void enable_percpu_irq(unsigned int irq, unsigned int type)
>  }
>  EXPORT_SYMBOL_GPL(enable_percpu_irq);
>  
> +void enable_percpu_nmi(unsigned int irq, unsigned int type)
> +{
> +	enable_percpu_irq(irq, type);
> +}
> +
>  /**
>   * irq_percpu_is_enabled - Check whether the per cpu irq is enabled
>   * @irq:	Linux irq number to check for
> @@ -2224,6 +2229,11 @@ void disable_percpu_irq(unsigned int irq)
>  }
>  EXPORT_SYMBOL_GPL(disable_percpu_irq);
>  
> +void disable_percpu_nmi(unsigned int irq)
> +{
> +	disable_percpu_irq(irq);
> +}
> +
>  /*
>   * Internal function to unregister a percpu irqaction.
>   */
> @@ -2255,6 +2265,8 @@ static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_
>  	/* Found it - now remove it from the list of entries: */
>  	desc->action = NULL;
>  
> +	desc->istate &= ~IRQS_NMI;
> +
>  	raw_spin_unlock_irqrestore(&desc->lock, flags);
>  
>  	unregister_handler_proc(irq, action);
> @@ -2308,6 +2320,19 @@ void free_percpu_irq(unsigned int irq, void __percpu *dev_id)
>  }
>  EXPORT_SYMBOL_GPL(free_percpu_irq);
>  
> +void free_percpu_nmi(unsigned int irq, void __percpu *dev_id)
> +{
> +	struct irq_desc *desc = irq_to_desc(irq);
> +
> +	if (!desc || !irq_settings_is_per_cpu_devid(desc))
> +		return;
> +
> +	if (WARN_ON(!(desc->istate & IRQS_NMI)))
> +		return;
> +
> +	kfree(__free_percpu_irq(irq, dev_id));
> +}
> +
>  /**
>   *	setup_percpu_irq - setup a per-cpu interrupt
>   *	@irq: Interrupt line to setup
> @@ -2398,6 +2423,129 @@ int __request_percpu_irq(unsigned int irq, irq_handler_t handler,
>  EXPORT_SYMBOL_GPL(__request_percpu_irq);
>  
>  /**
> + *	request_percpu_nmi - allocate a percpu interrupt line for NMI delivery
> + *	@irq: Interrupt line to allocate
> + *	@handler: Function to be called when the IRQ occurs.
> + *	@name: An ascii name for the claiming device
> + *	@dev_id: A percpu cookie passed back to the handler function
> + *
> + *	This call allocates interrupt resources for a per CPU NMI. Per CPU NMIs
> + *	have to be setup on each CPU by calling ready_percpu_nmi() before being
> + *	enabled on the same CPU by using enable_percpu_nmi().
> + *
> + *	Dev_id must be globally unique. It is a per-cpu variable, and
> + *	the handler gets called with the interrupted CPU's instance of
> + *	that variable.
> + *
> + *	Interrupt lines requested for NMI delivering should have auto enabling
> + *	setting disabled.
> + *
> + *	If the interrupt line cannot be used to deliver NMIs, function
> + *	will fail returning a negative value.
> + */
> +int request_percpu_nmi(unsigned int irq, irq_handler_t handler,
> +		       const char *name, void __percpu *dev_id)
> +{
> +	struct irqaction *action;
> +	struct irq_desc *desc;
> +	unsigned long flags;
> +	int retval;
> +
> +	if (!handler)
> +		return -EINVAL;
> +
> +	desc = irq_to_desc(irq);
> +
> +	if (!desc || !irq_settings_can_request(desc) ||
> +	    !irq_settings_is_per_cpu_devid(desc) ||
> +	    irq_settings_can_autoenable(desc) ||
> +	    !irq_supports_nmi(desc))
> +		return -EINVAL;
> +
> +	/* The line cannot already be NMI */
> +	if (desc->istate & IRQS_NMI)
> +		return -EINVAL;
> +
> +	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
> +	if (!action)
> +		return -ENOMEM;
> +
> +	action->handler = handler;
> +	action->flags = IRQF_PERCPU | IRQF_NO_SUSPEND | IRQF_NO_THREAD
> +		| IRQF_NOBALANCING;
> +	action->name = name;
> +	action->percpu_dev_id = dev_id;
> +
> +	retval = irq_chip_pm_get(&desc->irq_data);
> +	if (retval < 0)
> +		goto err_out;
> +
> +	retval = __setup_irq(irq, desc, action);
> +	if (retval)
> +		goto err_irq_setup;
> +
> +	raw_spin_lock_irqsave(&desc->lock, flags);
> +	desc->istate |= IRQS_NMI;
> +	raw_spin_unlock_irqrestore(&desc->lock, flags);
> +
> +	return 0;
> +
> +err_irq_setup:
> +	irq_chip_pm_put(&desc->irq_data);
> +err_out:
> +	kfree(action);
> +
> +	return retval;
> +}
> +
> +int ready_percpu_nmi(unsigned int irq)

Name issue withstanding, this could do with some documentation. You
probably want to indicate that this is expected to be called from a
non-preemptible section.

> +{
> +	unsigned long flags;
> +	struct irq_desc *desc = irq_get_desc_lock(irq, &flags,
> +						  IRQ_GET_DESC_CHECK_PERCPU);
> +	int ret = 0;
> +
> +	if (!desc) {
> +		ret = -EINVAL;
> +		goto out;

Ouch. Bad idea.

> +	}
> +
> +	if (WARN(!(desc->istate & IRQS_NMI),
> +		 KERN_ERR "ready_percpu_nmi called for a non-NMI interrupt: irq %u\n",
> +		 irq)) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	ret = irq_nmi_setup(desc);
> +	if (ret) {
> +		pr_err("Failed to setup NMI delivery: irq %u\n", irq);
> +		goto out;
> +	}
> +
> +out:
> +	irq_put_desc_unlock(desc, flags);
> +	return ret;
> +}
> +
> +void teardown_percpu_nmi(unsigned int irq)
> +{
> +	unsigned long flags;
> +	struct irq_desc *desc = irq_get_desc_lock(irq, &flags,
> +						  IRQ_GET_DESC_CHECK_PERCPU);
> +
> +	if (!desc)
> +		return;
> +
> +	if (WARN_ON(!(desc->istate & IRQS_NMI)))
> +		goto out;
> +
> +	irq_nmi_teardown(desc);
> +out:
> +	irq_put_desc_unlock(desc, flags);
> +}
> +
> +/**
>   *	irq_get_irqchip_state - returns the irqchip state of a interrupt.
>   *	@irq: Interrupt line that is forwarded to a VM
>   *	@which: One of IRQCHIP_STATE_* the caller wants to know about
> -- 
> 1.9.1
> 

Thanks,

	M.

-- 
Jazz is not dead, it just smell funny.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 3/4] genirq: Provide NMI handlers
  2019-01-28 15:38 ` [PATCH v5 3/4] genirq: Provide NMI handlers Julien Thierry
@ 2019-01-28 16:42   ` Marc Zyngier
  0 siblings, 0 replies; 11+ messages in thread
From: Marc Zyngier @ 2019-01-28 16:42 UTC (permalink / raw)
  To: Julien Thierry; +Cc: linux-kernel, tglx, peterz, mingo

On Mon, 28 Jan 2019 15:38:26 +0000,
Julien Thierry <julien.thierry@arm.com> wrote:
> 
> Provide flow handlers that are NMI safe for interrupts and percpu_devid
> interrupts.
> 
> Signed-off-by: Julien Thierry <julien.thierry@arm.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Cc: Peter Zijlstra <peterz@infradead.org>

Acked-by: Marc Zyngier <marc.zyngier@arm.com>

	M.

-- 
Jazz is not dead, it just smell funny.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 4/4] irqdesc: Add domain handler for NMIs
  2019-01-28 15:38 ` [PATCH v5 4/4] irqdesc: Add domain handler for NMIs Julien Thierry
@ 2019-01-28 16:46   ` Marc Zyngier
  0 siblings, 0 replies; 11+ messages in thread
From: Marc Zyngier @ 2019-01-28 16:46 UTC (permalink / raw)
  To: Julien Thierry; +Cc: linux-kernel, tglx, peterz, mingo, Will Deacon

On Mon, 28 Jan 2019 15:38:27 +0000,
Julien Thierry <julien.thierry@arm.com> wrote:
> 
> NMI handling code should be executed between calls to nmi_enter and
> nmi_exit.
> 
> Add a separate domain handler to properly setup NMI context when handling
> an interrupt requested as NMI.
> 
> Signed-off-by: Julien Thierry <julien.thierry@arm.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Peter Zijlstra <peterz@infradead.org>

Acked-by: Marc Zyngier <marc.zyngier@arm.com>

	M.

-- 
Jazz is not dead, it just smell funny.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 0/4] Provide core API for NMIs
  2019-01-28 15:38 [PATCH v5 0/4] Provide core API for NMIs Julien Thierry
                   ` (3 preceding siblings ...)
  2019-01-28 15:38 ` [PATCH v5 4/4] irqdesc: Add domain handler for NMIs Julien Thierry
@ 2019-01-28 16:54 ` Marc Zyngier
  4 siblings, 0 replies; 11+ messages in thread
From: Marc Zyngier @ 2019-01-28 16:54 UTC (permalink / raw)
  To: Julien Thierry; +Cc: linux-kernel, tglx, peterz, mingo

Hi Julien,

On Mon, 28 Jan 2019 15:38:23 +0000,
Julien Thierry <julien.thierry@arm.com> wrote:
> 
> Hi,
> 
> This patch series provides a way for irqchips to define some IRQs as NMIs.
> 
> Updating this series as it is needed for the arm64 pseudo-NMI[1] which we
> are considering to merge.
> 
> Changes since v4[2]:
> - Rebased on v5.0-rc4
> - Modified comment for request_percpu_nmi() to detail
>   the use of per CPU NMIs a bit more

I think this is starting to look good. If Thomas, Ingo and Peter are
OK with it, we should start talking about the merge strategy of the
humongous arm64 series that depends on this.

Thanks,

	M.

-- 
Jazz is not dead, it just smell funny.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v5 2/4] genirq: Provide NMI management for percpu_devid interrupts
  2019-01-28 16:41   ` Marc Zyngier
@ 2019-01-28 17:27     ` Julien Thierry
  0 siblings, 0 replies; 11+ messages in thread
From: Julien Thierry @ 2019-01-28 17:27 UTC (permalink / raw)
  To: Marc Zyngier; +Cc: linux-kernel, tglx, peterz, mingo



On 28/01/2019 16:41, Marc Zyngier wrote:
> On Mon, 28 Jan 2019 15:38:25 +0000,
> Julien Thierry <julien.thierry@arm.com> wrote:
>>
>> Add support for percpu_devid interrupts treated as NMIs.
>>
>> Percpu_devid NMIs need to be setup/torn down on each CPU they target.
>>
>> The same restrictions as for global NMIs still apply for percpu_devid NMIs.
> 
> A quick overall view of the new API would be good, specially as some
> of the bits are not 100% obvious.
> 
>>
>> Signed-off-by: Julien Thierry <julien.thierry@arm.com>
>> Cc: Thomas Gleixner <tglx@linutronix.de>
>> Cc: Peter Zijlstra <peterz@infradead.org>
>> Cc: Ingo Molnar <mingo@kernel.org>
>> Cc: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>>  include/linux/interrupt.h |   9 +++
>>  kernel/irq/manage.c       | 148 ++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 157 insertions(+)
>>
>> diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
>> index 37a4b0c6..be146bd 100644
>> --- a/include/linux/interrupt.h
>> +++ b/include/linux/interrupt.h
>> @@ -168,10 +168,15 @@ struct irqaction {
>>  				    devname, percpu_dev_id);
>>  }
>>  
>> +extern int __must_check
>> +request_percpu_nmi(unsigned int irq, irq_handler_t handler,
>> +		   const char *devname, void __percpu *dev);
>> +
>>  extern const void *free_irq(unsigned int, void *);
>>  extern void free_percpu_irq(unsigned int, void __percpu *);
>>  
>>  extern const void *free_nmi(unsigned int irq, void *dev_id);
>> +extern void free_percpu_nmi(unsigned int irq, void __percpu *percpu_dev_id);
>>  
>>  struct device;
>>  
>> @@ -224,7 +229,11 @@ struct irqaction {
>>  extern void irq_wake_thread(unsigned int irq, void *dev_id);
>>  
>>  extern void disable_nmi_nosync(unsigned int irq);
>> +extern void disable_percpu_nmi(unsigned int irq);
>>  extern void enable_nmi(unsigned int irq);
>> +extern void enable_percpu_nmi(unsigned int irq, unsigned int type);
>> +extern int ready_percpu_nmi(unsigned int irq);
> 
> ready_percpu_nmi seems a very bizarre name, as I cannot figure out
> what that does. How about something along the line of
> "setup_percpu_nmi"?
> 

The reason I did not go for "setup_percpu_nmi" is because I didn't want
it to be assimilated to "setup_irq"/"__setup_irq" which sets up all the
resources for an IRQ line.

I guess "prepare_percpu_nmi()" could do the trick unless I come up with
a better name.

>> +extern void teardown_percpu_nmi(unsigned int irq);
>>  
>>  /* The following three functions are for the core kernel use only. */
>>  extern void suspend_device_irqs(void);
>> diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
>> index a168b2d8f..602a622 100644
>> --- a/kernel/irq/manage.c
>> +++ b/kernel/irq/manage.c
>> @@ -2185,6 +2185,11 @@ void enable_percpu_irq(unsigned int irq, unsigned int type)
>>  }
>>  EXPORT_SYMBOL_GPL(enable_percpu_irq);
>>  
>> +void enable_percpu_nmi(unsigned int irq, unsigned int type)
>> +{
>> +	enable_percpu_irq(irq, type);
>> +}
>> +
>>  /**
>>   * irq_percpu_is_enabled - Check whether the per cpu irq is enabled
>>   * @irq:	Linux irq number to check for
>> @@ -2224,6 +2229,11 @@ void disable_percpu_irq(unsigned int irq)
>>  }
>>  EXPORT_SYMBOL_GPL(disable_percpu_irq);
>>  
>> +void disable_percpu_nmi(unsigned int irq)
>> +{
>> +	disable_percpu_irq(irq);
>> +}
>> +
>>  /*
>>   * Internal function to unregister a percpu irqaction.
>>   */
>> @@ -2255,6 +2265,8 @@ static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_
>>  	/* Found it - now remove it from the list of entries: */
>>  	desc->action = NULL;
>>  
>> +	desc->istate &= ~IRQS_NMI;
>> +
>>  	raw_spin_unlock_irqrestore(&desc->lock, flags);
>>  
>>  	unregister_handler_proc(irq, action);
>> @@ -2308,6 +2320,19 @@ void free_percpu_irq(unsigned int irq, void __percpu *dev_id)
>>  }
>>  EXPORT_SYMBOL_GPL(free_percpu_irq);
>>  
>> +void free_percpu_nmi(unsigned int irq, void __percpu *dev_id)
>> +{
>> +	struct irq_desc *desc = irq_to_desc(irq);
>> +
>> +	if (!desc || !irq_settings_is_per_cpu_devid(desc))
>> +		return;
>> +
>> +	if (WARN_ON(!(desc->istate & IRQS_NMI)))
>> +		return;
>> +
>> +	kfree(__free_percpu_irq(irq, dev_id));
>> +}
>> +
>>  /**
>>   *	setup_percpu_irq - setup a per-cpu interrupt
>>   *	@irq: Interrupt line to setup
>> @@ -2398,6 +2423,129 @@ int __request_percpu_irq(unsigned int irq, irq_handler_t handler,
>>  EXPORT_SYMBOL_GPL(__request_percpu_irq);
>>  
>>  /**
>> + *	request_percpu_nmi - allocate a percpu interrupt line for NMI delivery
>> + *	@irq: Interrupt line to allocate
>> + *	@handler: Function to be called when the IRQ occurs.
>> + *	@name: An ascii name for the claiming device
>> + *	@dev_id: A percpu cookie passed back to the handler function
>> + *
>> + *	This call allocates interrupt resources for a per CPU NMI. Per CPU NMIs
>> + *	have to be setup on each CPU by calling ready_percpu_nmi() before being
>> + *	enabled on the same CPU by using enable_percpu_nmi().
>> + *
>> + *	Dev_id must be globally unique. It is a per-cpu variable, and
>> + *	the handler gets called with the interrupted CPU's instance of
>> + *	that variable.
>> + *
>> + *	Interrupt lines requested for NMI delivering should have auto enabling
>> + *	setting disabled.
>> + *
>> + *	If the interrupt line cannot be used to deliver NMIs, function
>> + *	will fail returning a negative value.
>> + */
>> +int request_percpu_nmi(unsigned int irq, irq_handler_t handler,
>> +		       const char *name, void __percpu *dev_id)
>> +{
>> +	struct irqaction *action;
>> +	struct irq_desc *desc;
>> +	unsigned long flags;
>> +	int retval;
>> +
>> +	if (!handler)
>> +		return -EINVAL;
>> +
>> +	desc = irq_to_desc(irq);
>> +
>> +	if (!desc || !irq_settings_can_request(desc) ||
>> +	    !irq_settings_is_per_cpu_devid(desc) ||
>> +	    irq_settings_can_autoenable(desc) ||
>> +	    !irq_supports_nmi(desc))
>> +		return -EINVAL;
>> +
>> +	/* The line cannot already be NMI */
>> +	if (desc->istate & IRQS_NMI)
>> +		return -EINVAL;
>> +
>> +	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
>> +	if (!action)
>> +		return -ENOMEM;
>> +
>> +	action->handler = handler;
>> +	action->flags = IRQF_PERCPU | IRQF_NO_SUSPEND | IRQF_NO_THREAD
>> +		| IRQF_NOBALANCING;
>> +	action->name = name;
>> +	action->percpu_dev_id = dev_id;
>> +
>> +	retval = irq_chip_pm_get(&desc->irq_data);
>> +	if (retval < 0)
>> +		goto err_out;
>> +
>> +	retval = __setup_irq(irq, desc, action);
>> +	if (retval)
>> +		goto err_irq_setup;
>> +
>> +	raw_spin_lock_irqsave(&desc->lock, flags);
>> +	desc->istate |= IRQS_NMI;
>> +	raw_spin_unlock_irqrestore(&desc->lock, flags);
>> +
>> +	return 0;
>> +
>> +err_irq_setup:
>> +	irq_chip_pm_put(&desc->irq_data);
>> +err_out:
>> +	kfree(action);
>> +
>> +	return retval;
>> +}
>> +
>> +int ready_percpu_nmi(unsigned int irq)
> 
> Name issue withstanding, this could do with some documentation. You
> probably want to indicate that this is expected to be called from a
> non-preemptible section.
> 

Sure, I can elaborate a bit.

>> +{
>> +	unsigned long flags;
>> +	struct irq_desc *desc = irq_get_desc_lock(irq, &flags,
>> +						  IRQ_GET_DESC_CHECK_PERCPU);
>> +	int ret = 0;
>> +
>> +	if (!desc) {
>> +		ret = -EINVAL;
>> +		goto out;
> 
> Ouch. Bad idea.
> 

Oh yes, well spotted!

Thanks,

>> +	}
>> +
>> +	if (WARN(!(desc->istate & IRQS_NMI),
>> +		 KERN_ERR "ready_percpu_nmi called for a non-NMI interrupt: irq %u\n",
>> +		 irq)) {
>> +		ret = -EINVAL;
>> +		goto out;
>> +	}
>> +
>> +	ret = irq_nmi_setup(desc);
>> +	if (ret) {
>> +		pr_err("Failed to setup NMI delivery: irq %u\n", irq);
>> +		goto out;
>> +	}
>> +
>> +out:
>> +	irq_put_desc_unlock(desc, flags);
>> +	return ret;
>> +}
>> +
>> +void teardown_percpu_nmi(unsigned int irq)
>> +{
>> +	unsigned long flags;
>> +	struct irq_desc *desc = irq_get_desc_lock(irq, &flags,
>> +						  IRQ_GET_DESC_CHECK_PERCPU);
>> +
>> +	if (!desc)
>> +		return;
>> +
>> +	if (WARN_ON(!(desc->istate & IRQS_NMI)))
>> +		goto out;
>> +
>> +	irq_nmi_teardown(desc);
>> +out:
>> +	irq_put_desc_unlock(desc, flags);
>> +}
>> +
>> +/**
>>   *	irq_get_irqchip_state - returns the irqchip state of a interrupt.
>>   *	@irq: Interrupt line that is forwarded to a VM
>>   *	@which: One of IRQCHIP_STATE_* the caller wants to know about
>> -- 
>> 1.9.1
>>
> 
> Thanks,
> 
> 	M.
> 

-- 
Julien Thierry

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2019-01-28 17:27 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-28 15:38 [PATCH v5 0/4] Provide core API for NMIs Julien Thierry
2019-01-28 15:38 ` [PATCH v5 1/4] genirq: Provide basic NMI management for interrupt lines Julien Thierry
2019-01-28 16:25   ` Marc Zyngier
2019-01-28 15:38 ` [PATCH v5 2/4] genirq: Provide NMI management for percpu_devid interrupts Julien Thierry
2019-01-28 16:41   ` Marc Zyngier
2019-01-28 17:27     ` Julien Thierry
2019-01-28 15:38 ` [PATCH v5 3/4] genirq: Provide NMI handlers Julien Thierry
2019-01-28 16:42   ` Marc Zyngier
2019-01-28 15:38 ` [PATCH v5 4/4] irqdesc: Add domain handler for NMIs Julien Thierry
2019-01-28 16:46   ` Marc Zyngier
2019-01-28 16:54 ` [PATCH v5 0/4] Provide core API " Marc Zyngier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).