On Tue, Jul 24, 2018 at 12:07:05PM +0100, Julien Thierry 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. > > Signed-off-by: Julien Thierry <julien.thie...@arm.com> > Cc: Thomas Gleixner <t...@linutronix.de> > Cc: Peter Zijlstra <pet...@infradead.org> > Cc: Ingo Molnar <mi...@kernel.org> > Cc: Marc Zyngier <marc.zyng...@arm.com> > --- > include/linux/interrupt.h | 9 +++ > kernel/irq/manage.c | 149 > ++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 158 insertions(+) > > diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h > index 3b1a320..59d3877 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);
Shouldn't this be a disable_percpu_nmi_nosync() as disable_nmi_nosync()? > 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 3b87143..ad41c4d 100644 > --- a/kernel/irq/manage.c > +++ b/kernel/irq/manage.c > @@ -2162,6 +2162,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 > @@ -2201,6 +2206,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. > */ > @@ -2232,6 +2242,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); > @@ -2285,6 +2297,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 > @@ -2375,6 +2400,130 @@ 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. > + * @devname: An ascii name for the claiming device > + * @dev_id: A percpu cookie passed back to the handler function > + * > + * This call allocates interrupt resources and enables the > + * interrupt on the local CPU. If the interrupt is supposed to be > + * enabled on other CPUs, it has to be done on each CPU 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)) Shouldn't the logical operators go at the end of the line in a multi-line statement? > + 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; Shouldn't IRQF_NOBALANCING be aligned with IRQF_PERCPU; Thanks and BR, Ricardo