Skip to content

Commit 31d9d9b

Browse files
Marc ZyngierKAGA-KOKO
authored andcommitted
genirq: Add support for per-cpu dev_id interrupts
The ARM GIC interrupt controller offers per CPU interrupts (PPIs), which are usually used to connect local timers to each core. Each CPU has its own private interface to the GIC, and only sees the PPIs that are directly connect to it. While these timers are separate devices and have a separate interrupt line to a core, they all use the same IRQ number. For these devices, request_irq() is not the right API as it assumes that an IRQ number is visible by a number of CPUs (through the affinity setting), but makes it very awkward to express that an IRQ number can be handled by all CPUs, and yet be a different interrupt line on each CPU, requiring a different dev_id cookie to be passed back to the handler. The *_percpu_irq() functions is designed to overcome these limitations, by providing a per-cpu dev_id vector: int request_percpu_irq(unsigned int irq, irq_handler_t handler, const char *devname, void __percpu *percpu_dev_id); void free_percpu_irq(unsigned int, void __percpu *); int setup_percpu_irq(unsigned int irq, struct irqaction *new); void remove_percpu_irq(unsigned int irq, struct irqaction *act); void enable_percpu_irq(unsigned int irq); void disable_percpu_irq(unsigned int irq); The API has a number of limitations: - no interrupt sharing - no threading - common handler across all the CPUs Once the interrupt is requested using setup_percpu_irq() or request_percpu_irq(), it must be enabled by each core that wishes its local interrupt to be delivered. Based on an initial patch by Thomas Gleixner. Signed-off-by: Marc Zyngier <[email protected]> Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 60f96b4 commit 31d9d9b

File tree

8 files changed

+345
-34
lines changed

8 files changed

+345
-34
lines changed

include/linux/interrupt.h

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
9595
* @flags: flags (see IRQF_* above)
9696
* @name: name of the device
9797
* @dev_id: cookie to identify the device
98+
* @percpu_dev_id: cookie to identify the device
9899
* @next: pointer to the next irqaction for shared interrupts
99100
* @irq: interrupt number
100101
* @dir: pointer to the proc/irq/NN/name entry
@@ -104,17 +105,18 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
104105
* @thread_mask: bitmask for keeping track of @thread activity
105106
*/
106107
struct irqaction {
107-
irq_handler_t handler;
108-
unsigned long flags;
109-
void *dev_id;
110-
struct irqaction *next;
111-
int irq;
112-
irq_handler_t thread_fn;
113-
struct task_struct *thread;
114-
unsigned long thread_flags;
115-
unsigned long thread_mask;
116-
const char *name;
117-
struct proc_dir_entry *dir;
108+
irq_handler_t handler;
109+
unsigned long flags;
110+
void *dev_id;
111+
void __percpu *percpu_dev_id;
112+
struct irqaction *next;
113+
int irq;
114+
irq_handler_t thread_fn;
115+
struct task_struct *thread;
116+
unsigned long thread_flags;
117+
unsigned long thread_mask;
118+
const char *name;
119+
struct proc_dir_entry *dir;
118120
} ____cacheline_internodealigned_in_smp;
119121

120122
extern irqreturn_t no_action(int cpl, void *dev_id);
@@ -136,6 +138,10 @@ extern int __must_check
136138
request_any_context_irq(unsigned int irq, irq_handler_t handler,
137139
unsigned long flags, const char *name, void *dev_id);
138140

141+
extern int __must_check
142+
request_percpu_irq(unsigned int irq, irq_handler_t handler,
143+
const char *devname, void __percpu *percpu_dev_id);
144+
139145
extern void exit_irq_thread(void);
140146
#else
141147

@@ -164,10 +170,18 @@ request_any_context_irq(unsigned int irq, irq_handler_t handler,
164170
return request_irq(irq, handler, flags, name, dev_id);
165171
}
166172

173+
static inline int __must_check
174+
request_percpu_irq(unsigned int irq, irq_handler_t handler,
175+
const char *devname, void __percpu *percpu_dev_id)
176+
{
177+
return request_irq(irq, handler, 0, devname, percpu_dev_id);
178+
}
179+
167180
static inline void exit_irq_thread(void) { }
168181
#endif
169182

170183
extern void free_irq(unsigned int, void *);
184+
extern void free_percpu_irq(unsigned int, void __percpu *);
171185

172186
struct device;
173187

@@ -207,7 +221,9 @@ extern void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id);
207221

208222
extern void disable_irq_nosync(unsigned int irq);
209223
extern void disable_irq(unsigned int irq);
224+
extern void disable_percpu_irq(unsigned int irq);
210225
extern void enable_irq(unsigned int irq);
226+
extern void enable_percpu_irq(unsigned int irq);
211227

212228
/* The following three functions are for the core kernel use only. */
213229
#ifdef CONFIG_GENERIC_HARDIRQS

include/linux/irq.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ typedef void (*irq_preflow_handler_t)(struct irq_data *data);
6666
* IRQ_NO_BALANCING - Interrupt cannot be balanced (affinity set)
6767
* IRQ_MOVE_PCNTXT - Interrupt can be migrated from process context
6868
* IRQ_NESTED_TRHEAD - Interrupt nests into another thread
69+
* IRQ_PER_CPU_DEVID - Dev_id is a per-cpu variable
6970
*/
7071
enum {
7172
IRQ_TYPE_NONE = 0x00000000,
@@ -88,12 +89,13 @@ enum {
8889
IRQ_MOVE_PCNTXT = (1 << 14),
8990
IRQ_NESTED_THREAD = (1 << 15),
9091
IRQ_NOTHREAD = (1 << 16),
92+
IRQ_PER_CPU_DEVID = (1 << 17),
9193
};
9294

9395
#define IRQF_MODIFY_MASK \
9496
(IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
9597
IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
96-
IRQ_PER_CPU | IRQ_NESTED_THREAD)
98+
IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID)
9799

98100
#define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING)
99101

@@ -367,6 +369,8 @@ enum {
367369
struct irqaction;
368370
extern int setup_irq(unsigned int irq, struct irqaction *new);
369371
extern void remove_irq(unsigned int irq, struct irqaction *act);
372+
extern int setup_percpu_irq(unsigned int irq, struct irqaction *new);
373+
extern void remove_percpu_irq(unsigned int irq, struct irqaction *act);
370374

371375
extern void irq_cpu_online(void);
372376
extern void irq_cpu_offline(void);
@@ -394,6 +398,7 @@ extern void handle_edge_irq(unsigned int irq, struct irq_desc *desc);
394398
extern void handle_edge_eoi_irq(unsigned int irq, struct irq_desc *desc);
395399
extern void handle_simple_irq(unsigned int irq, struct irq_desc *desc);
396400
extern void handle_percpu_irq(unsigned int irq, struct irq_desc *desc);
401+
extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc);
397402
extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc);
398403
extern void handle_nested_irq(unsigned int irq);
399404

@@ -422,6 +427,8 @@ static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *c
422427
irq_set_chip_and_handler_name(irq, chip, handle, NULL);
423428
}
424429

430+
extern int irq_set_percpu_devid(unsigned int irq);
431+
425432
extern void
426433
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
427434
const char *name);
@@ -483,6 +490,13 @@ static inline void irq_set_nested_thread(unsigned int irq, bool nest)
483490
irq_clear_status_flags(irq, IRQ_NESTED_THREAD);
484491
}
485492

493+
static inline void irq_set_percpu_devid_flags(unsigned int irq)
494+
{
495+
irq_set_status_flags(irq,
496+
IRQ_NOAUTOEN | IRQ_PER_CPU | IRQ_NOTHREAD |
497+
IRQ_NOPROBE | IRQ_PER_CPU_DEVID);
498+
}
499+
486500
/* Handle dynamic irq creation and destruction */
487501
extern unsigned int create_irq_nr(unsigned int irq_want, int node);
488502
extern int create_irq(void);

include/linux/irqdesc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ struct irq_desc {
5353
unsigned long last_unhandled; /* Aging timer for unhandled count */
5454
unsigned int irqs_unhandled;
5555
raw_spinlock_t lock;
56+
struct cpumask *percpu_enabled;
5657
#ifdef CONFIG_SMP
5758
const struct cpumask *affinity_hint;
5859
struct irq_affinity_notify *affinity_notify;

kernel/irq/chip.c

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
2727
{
2828
unsigned long flags;
29-
struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
29+
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
3030

3131
if (!desc)
3232
return -EINVAL;
@@ -54,7 +54,7 @@ EXPORT_SYMBOL(irq_set_chip);
5454
int irq_set_irq_type(unsigned int irq, unsigned int type)
5555
{
5656
unsigned long flags;
57-
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
57+
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
5858
int ret = 0;
5959

6060
if (!desc)
@@ -78,7 +78,7 @@ EXPORT_SYMBOL(irq_set_irq_type);
7878
int irq_set_handler_data(unsigned int irq, void *data)
7979
{
8080
unsigned long flags;
81-
struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
81+
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
8282

8383
if (!desc)
8484
return -EINVAL;
@@ -98,7 +98,7 @@ EXPORT_SYMBOL(irq_set_handler_data);
9898
int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)
9999
{
100100
unsigned long flags;
101-
struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
101+
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
102102

103103
if (!desc)
104104
return -EINVAL;
@@ -119,7 +119,7 @@ int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)
119119
int irq_set_chip_data(unsigned int irq, void *data)
120120
{
121121
unsigned long flags;
122-
struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
122+
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
123123

124124
if (!desc)
125125
return -EINVAL;
@@ -204,6 +204,24 @@ void irq_disable(struct irq_desc *desc)
204204
}
205205
}
206206

207+
void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu)
208+
{
209+
if (desc->irq_data.chip->irq_enable)
210+
desc->irq_data.chip->irq_enable(&desc->irq_data);
211+
else
212+
desc->irq_data.chip->irq_unmask(&desc->irq_data);
213+
cpumask_set_cpu(cpu, desc->percpu_enabled);
214+
}
215+
216+
void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu)
217+
{
218+
if (desc->irq_data.chip->irq_disable)
219+
desc->irq_data.chip->irq_disable(&desc->irq_data);
220+
else
221+
desc->irq_data.chip->irq_mask(&desc->irq_data);
222+
cpumask_clear_cpu(cpu, desc->percpu_enabled);
223+
}
224+
207225
static inline void mask_ack_irq(struct irq_desc *desc)
208226
{
209227
if (desc->irq_data.chip->irq_mask_ack)
@@ -544,12 +562,44 @@ handle_percpu_irq(unsigned int irq, struct irq_desc *desc)
544562
chip->irq_eoi(&desc->irq_data);
545563
}
546564

565+
/**
566+
* handle_percpu_devid_irq - Per CPU local irq handler with per cpu dev ids
567+
* @irq: the interrupt number
568+
* @desc: the interrupt description structure for this irq
569+
*
570+
* Per CPU interrupts on SMP machines without locking requirements. Same as
571+
* handle_percpu_irq() above but with the following extras:
572+
*
573+
* action->percpu_dev_id is a pointer to percpu variables which
574+
* contain the real device id for the cpu on which this handler is
575+
* called
576+
*/
577+
void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc)
578+
{
579+
struct irq_chip *chip = irq_desc_get_chip(desc);
580+
struct irqaction *action = desc->action;
581+
void *dev_id = __this_cpu_ptr(action->percpu_dev_id);
582+
irqreturn_t res;
583+
584+
kstat_incr_irqs_this_cpu(irq, desc);
585+
586+
if (chip->irq_ack)
587+
chip->irq_ack(&desc->irq_data);
588+
589+
trace_irq_handler_entry(irq, action);
590+
res = action->handler(irq, dev_id);
591+
trace_irq_handler_exit(irq, action, res);
592+
593+
if (chip->irq_eoi)
594+
chip->irq_eoi(&desc->irq_data);
595+
}
596+
547597
void
548598
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
549599
const char *name)
550600
{
551601
unsigned long flags;
552-
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
602+
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
553603

554604
if (!desc)
555605
return;
@@ -593,7 +643,7 @@ irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
593643
void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
594644
{
595645
unsigned long flags;
596-
struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
646+
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
597647

598648
if (!desc)
599649
return;

kernel/irq/internals.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ extern int irq_startup(struct irq_desc *desc);
7171
extern void irq_shutdown(struct irq_desc *desc);
7272
extern void irq_enable(struct irq_desc *desc);
7373
extern void irq_disable(struct irq_desc *desc);
74+
extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu);
75+
extern void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu);
7476
extern void mask_irq(struct irq_desc *desc);
7577
extern void unmask_irq(struct irq_desc *desc);
7678

@@ -114,14 +116,21 @@ static inline void chip_bus_sync_unlock(struct irq_desc *desc)
114116
desc->irq_data.chip->irq_bus_sync_unlock(&desc->irq_data);
115117
}
116118

119+
#define _IRQ_DESC_CHECK (1 << 0)
120+
#define _IRQ_DESC_PERCPU (1 << 1)
121+
122+
#define IRQ_GET_DESC_CHECK_GLOBAL (_IRQ_DESC_CHECK)
123+
#define IRQ_GET_DESC_CHECK_PERCPU (_IRQ_DESC_CHECK | _IRQ_DESC_PERCPU)
124+
117125
struct irq_desc *
118-
__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus);
126+
__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
127+
unsigned int check);
119128
void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus);
120129

121130
static inline struct irq_desc *
122-
irq_get_desc_buslock(unsigned int irq, unsigned long *flags)
131+
irq_get_desc_buslock(unsigned int irq, unsigned long *flags, unsigned int check)
123132
{
124-
return __irq_get_desc_lock(irq, flags, true);
133+
return __irq_get_desc_lock(irq, flags, true, check);
125134
}
126135

127136
static inline void
@@ -131,9 +140,9 @@ irq_put_desc_busunlock(struct irq_desc *desc, unsigned long flags)
131140
}
132141

133142
static inline struct irq_desc *
134-
irq_get_desc_lock(unsigned int irq, unsigned long *flags)
143+
irq_get_desc_lock(unsigned int irq, unsigned long *flags, unsigned int check)
135144
{
136-
return __irq_get_desc_lock(irq, flags, false);
145+
return __irq_get_desc_lock(irq, flags, false, check);
137146
}
138147

139148
static inline void

kernel/irq/irqdesc.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,11 +424,22 @@ unsigned int irq_get_next_irq(unsigned int offset)
424424
}
425425

426426
struct irq_desc *
427-
__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus)
427+
__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
428+
unsigned int check)
428429
{
429430
struct irq_desc *desc = irq_to_desc(irq);
430431

431432
if (desc) {
433+
if (check & _IRQ_DESC_CHECK) {
434+
if ((check & _IRQ_DESC_PERCPU) &&
435+
!irq_settings_is_per_cpu_devid(desc))
436+
return NULL;
437+
438+
if (!(check & _IRQ_DESC_PERCPU) &&
439+
irq_settings_is_per_cpu_devid(desc))
440+
return NULL;
441+
}
442+
432443
if (bus)
433444
chip_bus_lock(desc);
434445
raw_spin_lock_irqsave(&desc->lock, *flags);
@@ -443,6 +454,25 @@ void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus)
443454
chip_bus_sync_unlock(desc);
444455
}
445456

457+
int irq_set_percpu_devid(unsigned int irq)
458+
{
459+
struct irq_desc *desc = irq_to_desc(irq);
460+
461+
if (!desc)
462+
return -EINVAL;
463+
464+
if (desc->percpu_enabled)
465+
return -EINVAL;
466+
467+
desc->percpu_enabled = kzalloc(sizeof(*desc->percpu_enabled), GFP_KERNEL);
468+
469+
if (!desc->percpu_enabled)
470+
return -ENOMEM;
471+
472+
irq_set_percpu_devid_flags(irq);
473+
return 0;
474+
}
475+
446476
/**
447477
* dynamic_irq_cleanup - cleanup a dynamically allocated irq
448478
* @irq: irq number to initialize

0 commit comments

Comments
 (0)