Skip to content

Commit e26a433

Browse files
committed
Merge branch 'irq/generic-nmi' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms
* 'irq/generic-nmi' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms: irqdesc: Add domain handler for NMIs genirq: Provide NMI handlers genirq: Provide NMI management for percpu_devid interrupts genirq: Provide basic NMI management for interrupt lines
2 parents 8aa67d1 + 6e4933a commit e26a433

File tree

8 files changed

+532
-3
lines changed

8 files changed

+532
-3
lines changed

include/linux/interrupt.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ __request_percpu_irq(unsigned int irq, irq_handler_t handler,
156156
unsigned long flags, const char *devname,
157157
void __percpu *percpu_dev_id);
158158

159+
extern int __must_check
160+
request_nmi(unsigned int irq, irq_handler_t handler, unsigned long flags,
161+
const char *name, void *dev);
162+
159163
static inline int __must_check
160164
request_percpu_irq(unsigned int irq, irq_handler_t handler,
161165
const char *devname, void __percpu *percpu_dev_id)
@@ -164,9 +168,16 @@ request_percpu_irq(unsigned int irq, irq_handler_t handler,
164168
devname, percpu_dev_id);
165169
}
166170

171+
extern int __must_check
172+
request_percpu_nmi(unsigned int irq, irq_handler_t handler,
173+
const char *devname, void __percpu *dev);
174+
167175
extern const void *free_irq(unsigned int, void *);
168176
extern void free_percpu_irq(unsigned int, void __percpu *);
169177

178+
extern const void *free_nmi(unsigned int irq, void *dev_id);
179+
extern void free_percpu_nmi(unsigned int irq, void __percpu *percpu_dev_id);
180+
170181
struct device;
171182

172183
extern int __must_check
@@ -217,6 +228,13 @@ extern void enable_percpu_irq(unsigned int irq, unsigned int type);
217228
extern bool irq_percpu_is_enabled(unsigned int irq);
218229
extern void irq_wake_thread(unsigned int irq, void *dev_id);
219230

231+
extern void disable_nmi_nosync(unsigned int irq);
232+
extern void disable_percpu_nmi(unsigned int irq);
233+
extern void enable_nmi(unsigned int irq);
234+
extern void enable_percpu_nmi(unsigned int irq, unsigned int type);
235+
extern int prepare_percpu_nmi(unsigned int irq);
236+
extern void teardown_percpu_nmi(unsigned int irq);
237+
220238
/* The following three functions are for the core kernel use only. */
221239
extern void suspend_device_irqs(void);
222240
extern void resume_device_irqs(void);

include/linux/irq.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,8 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
442442
* @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine
443443
* @ipi_send_single: send a single IPI to destination cpus
444444
* @ipi_send_mask: send an IPI to destination cpus in cpumask
445+
* @irq_nmi_setup: function called from core code before enabling an NMI
446+
* @irq_nmi_teardown: function called from core code after disabling an NMI
445447
* @flags: chip specific flags
446448
*/
447449
struct irq_chip {
@@ -490,6 +492,9 @@ struct irq_chip {
490492
void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
491493
void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
492494

495+
int (*irq_nmi_setup)(struct irq_data *data);
496+
void (*irq_nmi_teardown)(struct irq_data *data);
497+
493498
unsigned long flags;
494499
};
495500

@@ -505,6 +510,7 @@ struct irq_chip {
505510
* IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask
506511
* IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode
507512
* IRQCHIP_SUPPORTS_LEVEL_MSI Chip can provide two doorbells for Level MSIs
513+
* IRQCHIP_SUPPORTS_NMI: Chip can deliver NMIs, only for root irqchips
508514
*/
509515
enum {
510516
IRQCHIP_SET_TYPE_MASKED = (1 << 0),
@@ -515,6 +521,7 @@ enum {
515521
IRQCHIP_ONESHOT_SAFE = (1 << 5),
516522
IRQCHIP_EOI_THREADED = (1 << 6),
517523
IRQCHIP_SUPPORTS_LEVEL_MSI = (1 << 7),
524+
IRQCHIP_SUPPORTS_NMI = (1 << 8),
518525
};
519526

520527
#include <linux/irqdesc.h>
@@ -594,6 +601,9 @@ extern void handle_percpu_devid_irq(struct irq_desc *desc);
594601
extern void handle_bad_irq(struct irq_desc *desc);
595602
extern void handle_nested_irq(unsigned int irq);
596603

604+
extern void handle_fasteoi_nmi(struct irq_desc *desc);
605+
extern void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc);
606+
597607
extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
598608
extern int irq_chip_pm_get(struct irq_data *data);
599609
extern int irq_chip_pm_put(struct irq_data *data);

include/linux/irqdesc.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ static inline int handle_domain_irq(struct irq_domain *domain,
171171
{
172172
return __handle_domain_irq(domain, hwirq, true, regs);
173173
}
174+
175+
#ifdef CONFIG_IRQ_DOMAIN
176+
int handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq,
177+
struct pt_regs *regs);
178+
#endif
174179
#endif
175180

176181
/* Test to see if a driver has successfully requested an irq */

kernel/irq/chip.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,37 @@ void handle_fasteoi_irq(struct irq_desc *desc)
729729
}
730730
EXPORT_SYMBOL_GPL(handle_fasteoi_irq);
731731

732+
/**
733+
* handle_fasteoi_nmi - irq handler for NMI interrupt lines
734+
* @desc: the interrupt description structure for this irq
735+
*
736+
* A simple NMI-safe handler, considering the restrictions
737+
* from request_nmi.
738+
*
739+
* Only a single callback will be issued to the chip: an ->eoi()
740+
* call when the interrupt has been serviced. This enables support
741+
* for modern forms of interrupt handlers, which handle the flow
742+
* details in hardware, transparently.
743+
*/
744+
void handle_fasteoi_nmi(struct irq_desc *desc)
745+
{
746+
struct irq_chip *chip = irq_desc_get_chip(desc);
747+
struct irqaction *action = desc->action;
748+
unsigned int irq = irq_desc_get_irq(desc);
749+
irqreturn_t res;
750+
751+
trace_irq_handler_entry(irq, action);
752+
/*
753+
* NMIs cannot be shared, there is only one action.
754+
*/
755+
res = action->handler(irq, action->dev_id);
756+
trace_irq_handler_exit(irq, action, res);
757+
758+
if (chip->irq_eoi)
759+
chip->irq_eoi(&desc->irq_data);
760+
}
761+
EXPORT_SYMBOL_GPL(handle_fasteoi_nmi);
762+
732763
/**
733764
* handle_edge_irq - edge type IRQ handler
734765
* @desc: the interrupt description structure for this irq
@@ -908,6 +939,29 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
908939
chip->irq_eoi(&desc->irq_data);
909940
}
910941

942+
/**
943+
* handle_percpu_devid_fasteoi_nmi - Per CPU local NMI handler with per cpu
944+
* dev ids
945+
* @desc: the interrupt description structure for this irq
946+
*
947+
* Similar to handle_fasteoi_nmi, but handling the dev_id cookie
948+
* as a percpu pointer.
949+
*/
950+
void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc)
951+
{
952+
struct irq_chip *chip = irq_desc_get_chip(desc);
953+
struct irqaction *action = desc->action;
954+
unsigned int irq = irq_desc_get_irq(desc);
955+
irqreturn_t res;
956+
957+
trace_irq_handler_entry(irq, action);
958+
res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
959+
trace_irq_handler_exit(irq, action, res);
960+
961+
if (chip->irq_eoi)
962+
chip->irq_eoi(&desc->irq_data);
963+
}
964+
911965
static void
912966
__irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
913967
int is_chained, const char *name)

kernel/irq/debugfs.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ static const struct irq_bit_descr irqchip_flags[] = {
5656
BIT_MASK_DESCR(IRQCHIP_ONESHOT_SAFE),
5757
BIT_MASK_DESCR(IRQCHIP_EOI_THREADED),
5858
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI),
59+
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_NMI),
5960
};
6061

6162
static void
@@ -140,6 +141,7 @@ static const struct irq_bit_descr irqdesc_istates[] = {
140141
BIT_MASK_DESCR(IRQS_WAITING),
141142
BIT_MASK_DESCR(IRQS_PENDING),
142143
BIT_MASK_DESCR(IRQS_SUSPENDED),
144+
BIT_MASK_DESCR(IRQS_NMI),
143145
};
144146

145147

@@ -203,8 +205,8 @@ static ssize_t irq_debug_write(struct file *file, const char __user *user_buf,
203205
chip_bus_lock(desc);
204206
raw_spin_lock_irqsave(&desc->lock, flags);
205207

206-
if (irq_settings_is_level(desc)) {
207-
/* Can't do level, sorry */
208+
if (irq_settings_is_level(desc) || desc->istate & IRQS_NMI) {
209+
/* Can't do level nor NMIs, sorry */
208210
err = -EINVAL;
209211
} else {
210212
desc->istate |= IRQS_PENDING;

kernel/irq/internals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ enum {
4949
* IRQS_WAITING - irq is waiting
5050
* IRQS_PENDING - irq is pending and replayed later
5151
* IRQS_SUSPENDED - irq is suspended
52+
* IRQS_NMI - irq line is used to deliver NMIs
5253
*/
5354
enum {
5455
IRQS_AUTODETECT = 0x00000001,
@@ -60,6 +61,7 @@ enum {
6061
IRQS_PENDING = 0x00000200,
6162
IRQS_SUSPENDED = 0x00000800,
6263
IRQS_TIMINGS = 0x00001000,
64+
IRQS_NMI = 0x00002000,
6365
};
6466

6567
#include "debug.h"

kernel/irq/irqdesc.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,41 @@ int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
669669
set_irq_regs(old_regs);
670670
return ret;
671671
}
672+
673+
#ifdef CONFIG_IRQ_DOMAIN
674+
/**
675+
* handle_domain_nmi - Invoke the handler for a HW irq belonging to a domain
676+
* @domain: The domain where to perform the lookup
677+
* @hwirq: The HW irq number to convert to a logical one
678+
* @regs: Register file coming from the low-level handling code
679+
*
680+
* Returns: 0 on success, or -EINVAL if conversion has failed
681+
*/
682+
int handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq,
683+
struct pt_regs *regs)
684+
{
685+
struct pt_regs *old_regs = set_irq_regs(regs);
686+
unsigned int irq;
687+
int ret = 0;
688+
689+
nmi_enter();
690+
691+
irq = irq_find_mapping(domain, hwirq);
692+
693+
/*
694+
* ack_bad_irq is not NMI-safe, just report
695+
* an invalid interrupt.
696+
*/
697+
if (likely(irq))
698+
generic_handle_irq(irq);
699+
else
700+
ret = -EINVAL;
701+
702+
nmi_exit();
703+
set_irq_regs(old_regs);
704+
return ret;
705+
}
706+
#endif
672707
#endif
673708

674709
/* Dynamic interrupt handling */

0 commit comments

Comments
 (0)