diff options
-rw-r--r-- | include/linux/interrupt.h | 38 | ||||
-rw-r--r-- | include/linux/irq.h | 16 | ||||
-rw-r--r-- | include/linux/irqdesc.h | 1 | ||||
-rw-r--r-- | kernel/irq/chip.c | 64 | ||||
-rw-r--r-- | kernel/irq/internals.h | 19 | ||||
-rw-r--r-- | kernel/irq/irqdesc.c | 32 | ||||
-rw-r--r-- | kernel/irq/manage.c | 202 | ||||
-rw-r--r-- | kernel/irq/settings.h | 7 |
8 files changed, 345 insertions, 34 deletions
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index a103732b7588..1cdfd09c8abc 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
@@ -95,6 +95,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *); | |||
95 | * @flags: flags (see IRQF_* above) | 95 | * @flags: flags (see IRQF_* above) |
96 | * @name: name of the device | 96 | * @name: name of the device |
97 | * @dev_id: cookie to identify the device | 97 | * @dev_id: cookie to identify the device |
98 | * @percpu_dev_id: cookie to identify the device | ||
98 | * @next: pointer to the next irqaction for shared interrupts | 99 | * @next: pointer to the next irqaction for shared interrupts |
99 | * @irq: interrupt number | 100 | * @irq: interrupt number |
100 | * @dir: pointer to the proc/irq/NN/name entry | 101 | * @dir: pointer to the proc/irq/NN/name entry |
@@ -104,17 +105,18 @@ typedef irqreturn_t (*irq_handler_t)(int, void *); | |||
104 | * @thread_mask: bitmask for keeping track of @thread activity | 105 | * @thread_mask: bitmask for keeping track of @thread activity |
105 | */ | 106 | */ |
106 | struct irqaction { | 107 | struct irqaction { |
107 | irq_handler_t handler; | 108 | irq_handler_t handler; |
108 | unsigned long flags; | 109 | unsigned long flags; |
109 | void *dev_id; | 110 | void *dev_id; |
110 | struct irqaction *next; | 111 | void __percpu *percpu_dev_id; |
111 | int irq; | 112 | struct irqaction *next; |
112 | irq_handler_t thread_fn; | 113 | int irq; |
113 | struct task_struct *thread; | 114 | irq_handler_t thread_fn; |
114 | unsigned long thread_flags; | 115 | struct task_struct *thread; |
115 | unsigned long thread_mask; | 116 | unsigned long thread_flags; |
116 | const char *name; | 117 | unsigned long thread_mask; |
117 | struct proc_dir_entry *dir; | 118 | const char *name; |
119 | struct proc_dir_entry *dir; | ||
118 | } ____cacheline_internodealigned_in_smp; | 120 | } ____cacheline_internodealigned_in_smp; |
119 | 121 | ||
120 | extern irqreturn_t no_action(int cpl, void *dev_id); | 122 | extern irqreturn_t no_action(int cpl, void *dev_id); |
@@ -136,6 +138,10 @@ extern int __must_check | |||
136 | request_any_context_irq(unsigned int irq, irq_handler_t handler, | 138 | request_any_context_irq(unsigned int irq, irq_handler_t handler, |
137 | unsigned long flags, const char *name, void *dev_id); | 139 | unsigned long flags, const char *name, void *dev_id); |
138 | 140 | ||
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 | |||
139 | extern void exit_irq_thread(void); | 145 | extern void exit_irq_thread(void); |
140 | #else | 146 | #else |
141 | 147 | ||
@@ -164,10 +170,18 @@ request_any_context_irq(unsigned int irq, irq_handler_t handler, | |||
164 | return request_irq(irq, handler, flags, name, dev_id); | 170 | return request_irq(irq, handler, flags, name, dev_id); |
165 | } | 171 | } |
166 | 172 | ||
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 | |||
167 | static inline void exit_irq_thread(void) { } | 180 | static inline void exit_irq_thread(void) { } |
168 | #endif | 181 | #endif |
169 | 182 | ||
170 | extern void free_irq(unsigned int, void *); | 183 | extern void free_irq(unsigned int, void *); |
184 | extern void free_percpu_irq(unsigned int, void __percpu *); | ||
171 | 185 | ||
172 | struct device; | 186 | struct device; |
173 | 187 | ||
@@ -207,7 +221,9 @@ extern void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id); | |||
207 | 221 | ||
208 | extern void disable_irq_nosync(unsigned int irq); | 222 | extern void disable_irq_nosync(unsigned int irq); |
209 | extern void disable_irq(unsigned int irq); | 223 | extern void disable_irq(unsigned int irq); |
224 | extern void disable_percpu_irq(unsigned int irq); | ||
210 | extern void enable_irq(unsigned int irq); | 225 | extern void enable_irq(unsigned int irq); |
226 | extern void enable_percpu_irq(unsigned int irq); | ||
211 | 227 | ||
212 | /* The following three functions are for the core kernel use only. */ | 228 | /* The following three functions are for the core kernel use only. */ |
213 | #ifdef CONFIG_GENERIC_HARDIRQS | 229 | #ifdef CONFIG_GENERIC_HARDIRQS |
diff --git a/include/linux/irq.h b/include/linux/irq.h index 73e31abeba1c..59e49c80cc2c 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h | |||
@@ -66,6 +66,7 @@ typedef void (*irq_preflow_handler_t)(struct irq_data *data); | |||
66 | * IRQ_NO_BALANCING - Interrupt cannot be balanced (affinity set) | 66 | * IRQ_NO_BALANCING - Interrupt cannot be balanced (affinity set) |
67 | * IRQ_MOVE_PCNTXT - Interrupt can be migrated from process context | 67 | * IRQ_MOVE_PCNTXT - Interrupt can be migrated from process context |
68 | * IRQ_NESTED_TRHEAD - Interrupt nests into another thread | 68 | * IRQ_NESTED_TRHEAD - Interrupt nests into another thread |
69 | * IRQ_PER_CPU_DEVID - Dev_id is a per-cpu variable | ||
69 | */ | 70 | */ |
70 | enum { | 71 | enum { |
71 | IRQ_TYPE_NONE = 0x00000000, | 72 | IRQ_TYPE_NONE = 0x00000000, |
@@ -88,12 +89,13 @@ enum { | |||
88 | IRQ_MOVE_PCNTXT = (1 << 14), | 89 | IRQ_MOVE_PCNTXT = (1 << 14), |
89 | IRQ_NESTED_THREAD = (1 << 15), | 90 | IRQ_NESTED_THREAD = (1 << 15), |
90 | IRQ_NOTHREAD = (1 << 16), | 91 | IRQ_NOTHREAD = (1 << 16), |
92 | IRQ_PER_CPU_DEVID = (1 << 17), | ||
91 | }; | 93 | }; |
92 | 94 | ||
93 | #define IRQF_MODIFY_MASK \ | 95 | #define IRQF_MODIFY_MASK \ |
94 | (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ | 96 | (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ |
95 | IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \ | 97 | 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) |
97 | 99 | ||
98 | #define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING) | 100 | #define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING) |
99 | 101 | ||
@@ -367,6 +369,8 @@ enum { | |||
367 | struct irqaction; | 369 | struct irqaction; |
368 | extern int setup_irq(unsigned int irq, struct irqaction *new); | 370 | extern int setup_irq(unsigned int irq, struct irqaction *new); |
369 | extern void remove_irq(unsigned int irq, struct irqaction *act); | 371 | 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); | ||
370 | 374 | ||
371 | extern void irq_cpu_online(void); | 375 | extern void irq_cpu_online(void); |
372 | extern void irq_cpu_offline(void); | 376 | extern void irq_cpu_offline(void); |
@@ -394,6 +398,7 @@ extern void handle_edge_irq(unsigned int irq, struct irq_desc *desc); | |||
394 | extern void handle_edge_eoi_irq(unsigned int irq, struct irq_desc *desc); | 398 | extern void handle_edge_eoi_irq(unsigned int irq, struct irq_desc *desc); |
395 | extern void handle_simple_irq(unsigned int irq, struct irq_desc *desc); | 399 | extern void handle_simple_irq(unsigned int irq, struct irq_desc *desc); |
396 | extern void handle_percpu_irq(unsigned int irq, struct irq_desc *desc); | 400 | 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); | ||
397 | extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc); | 402 | extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc); |
398 | extern void handle_nested_irq(unsigned int irq); | 403 | extern void handle_nested_irq(unsigned int irq); |
399 | 404 | ||
@@ -422,6 +427,8 @@ static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *c | |||
422 | irq_set_chip_and_handler_name(irq, chip, handle, NULL); | 427 | irq_set_chip_and_handler_name(irq, chip, handle, NULL); |
423 | } | 428 | } |
424 | 429 | ||
430 | extern int irq_set_percpu_devid(unsigned int irq); | ||
431 | |||
425 | extern void | 432 | extern void |
426 | __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, | 433 | __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, |
427 | const char *name); | 434 | const char *name); |
@@ -483,6 +490,13 @@ static inline void irq_set_nested_thread(unsigned int irq, bool nest) | |||
483 | irq_clear_status_flags(irq, IRQ_NESTED_THREAD); | 490 | irq_clear_status_flags(irq, IRQ_NESTED_THREAD); |
484 | } | 491 | } |
485 | 492 | ||
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 | |||
486 | /* Handle dynamic irq creation and destruction */ | 500 | /* Handle dynamic irq creation and destruction */ |
487 | extern unsigned int create_irq_nr(unsigned int irq_want, int node); | 501 | extern unsigned int create_irq_nr(unsigned int irq_want, int node); |
488 | extern int create_irq(void); | 502 | extern int create_irq(void); |
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 150134ac709a..6b69c2c9dff1 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h | |||
@@ -53,6 +53,7 @@ struct irq_desc { | |||
53 | unsigned long last_unhandled; /* Aging timer for unhandled count */ | 53 | unsigned long last_unhandled; /* Aging timer for unhandled count */ |
54 | unsigned int irqs_unhandled; | 54 | unsigned int irqs_unhandled; |
55 | raw_spinlock_t lock; | 55 | raw_spinlock_t lock; |
56 | struct cpumask *percpu_enabled; | ||
56 | #ifdef CONFIG_SMP | 57 | #ifdef CONFIG_SMP |
57 | const struct cpumask *affinity_hint; | 58 | const struct cpumask *affinity_hint; |
58 | struct irq_affinity_notify *affinity_notify; | 59 | struct irq_affinity_notify *affinity_notify; |
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index dc5114b4c16c..f7c543a801d9 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c | |||
@@ -26,7 +26,7 @@ | |||
26 | int irq_set_chip(unsigned int irq, struct irq_chip *chip) | 26 | int irq_set_chip(unsigned int irq, struct irq_chip *chip) |
27 | { | 27 | { |
28 | unsigned long flags; | 28 | 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); |
30 | 30 | ||
31 | if (!desc) | 31 | if (!desc) |
32 | return -EINVAL; | 32 | return -EINVAL; |
@@ -54,7 +54,7 @@ EXPORT_SYMBOL(irq_set_chip); | |||
54 | int irq_set_irq_type(unsigned int irq, unsigned int type) | 54 | int irq_set_irq_type(unsigned int irq, unsigned int type) |
55 | { | 55 | { |
56 | unsigned long flags; | 56 | 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); |
58 | int ret = 0; | 58 | int ret = 0; |
59 | 59 | ||
60 | if (!desc) | 60 | if (!desc) |
@@ -78,7 +78,7 @@ EXPORT_SYMBOL(irq_set_irq_type); | |||
78 | int irq_set_handler_data(unsigned int irq, void *data) | 78 | int irq_set_handler_data(unsigned int irq, void *data) |
79 | { | 79 | { |
80 | unsigned long flags; | 80 | 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); |
82 | 82 | ||
83 | if (!desc) | 83 | if (!desc) |
84 | return -EINVAL; | 84 | return -EINVAL; |
@@ -98,7 +98,7 @@ EXPORT_SYMBOL(irq_set_handler_data); | |||
98 | int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry) | 98 | int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry) |
99 | { | 99 | { |
100 | unsigned long flags; | 100 | 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); |
102 | 102 | ||
103 | if (!desc) | 103 | if (!desc) |
104 | return -EINVAL; | 104 | return -EINVAL; |
@@ -119,7 +119,7 @@ int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry) | |||
119 | int irq_set_chip_data(unsigned int irq, void *data) | 119 | int irq_set_chip_data(unsigned int irq, void *data) |
120 | { | 120 | { |
121 | unsigned long flags; | 121 | 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); |
123 | 123 | ||
124 | if (!desc) | 124 | if (!desc) |
125 | return -EINVAL; | 125 | return -EINVAL; |
@@ -204,6 +204,24 @@ void irq_disable(struct irq_desc *desc) | |||
204 | } | 204 | } |
205 | } | 205 | } |
206 | 206 | ||
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 | |||
207 | static inline void mask_ack_irq(struct irq_desc *desc) | 225 | static inline void mask_ack_irq(struct irq_desc *desc) |
208 | { | 226 | { |
209 | if (desc->irq_data.chip->irq_mask_ack) | 227 | if (desc->irq_data.chip->irq_mask_ack) |
@@ -544,12 +562,44 @@ handle_percpu_irq(unsigned int irq, struct irq_desc *desc) | |||
544 | chip->irq_eoi(&desc->irq_data); | 562 | chip->irq_eoi(&desc->irq_data); |
545 | } | 563 | } |
546 | 564 | ||
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 | |||
547 | void | 597 | void |
548 | __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, | 598 | __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, |
549 | const char *name) | 599 | const char *name) |
550 | { | 600 | { |
551 | unsigned long flags; | 601 | 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); |
553 | 603 | ||
554 | if (!desc) | 604 | if (!desc) |
555 | return; | 605 | return; |
@@ -593,7 +643,7 @@ irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, | |||
593 | void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set) | 643 | void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set) |
594 | { | 644 | { |
595 | unsigned long flags; | 645 | 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); |
597 | 647 | ||
598 | if (!desc) | 648 | if (!desc) |
599 | return; | 649 | return; |
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 6546431447d7..a73dd6c7372d 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h | |||
@@ -71,6 +71,8 @@ extern int irq_startup(struct irq_desc *desc); | |||
71 | extern void irq_shutdown(struct irq_desc *desc); | 71 | extern void irq_shutdown(struct irq_desc *desc); |
72 | extern void irq_enable(struct irq_desc *desc); | 72 | extern void irq_enable(struct irq_desc *desc); |
73 | extern void irq_disable(struct irq_desc *desc); | 73 | 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); | ||
74 | extern void mask_irq(struct irq_desc *desc); | 76 | extern void mask_irq(struct irq_desc *desc); |
75 | extern void unmask_irq(struct irq_desc *desc); | 77 | extern void unmask_irq(struct irq_desc *desc); |
76 | 78 | ||
@@ -114,14 +116,21 @@ static inline void chip_bus_sync_unlock(struct irq_desc *desc) | |||
114 | desc->irq_data.chip->irq_bus_sync_unlock(&desc->irq_data); | 116 | desc->irq_data.chip->irq_bus_sync_unlock(&desc->irq_data); |
115 | } | 117 | } |
116 | 118 | ||
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 | |||
117 | struct irq_desc * | 125 | 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); | ||
119 | void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus); | 128 | void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus); |
120 | 129 | ||
121 | static inline struct irq_desc * | 130 | 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) |
123 | { | 132 | { |
124 | return __irq_get_desc_lock(irq, flags, true); | 133 | return __irq_get_desc_lock(irq, flags, true, check); |
125 | } | 134 | } |
126 | 135 | ||
127 | static inline void | 136 | static inline void |
@@ -131,9 +140,9 @@ irq_put_desc_busunlock(struct irq_desc *desc, unsigned long flags) | |||
131 | } | 140 | } |
132 | 141 | ||
133 | static inline struct irq_desc * | 142 | 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) |
135 | { | 144 | { |
136 | return __irq_get_desc_lock(irq, flags, false); | 145 | return __irq_get_desc_lock(irq, flags, false, check); |
137 | } | 146 | } |
138 | 147 | ||
139 | static inline void | 148 | static inline void |
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 039b889ea053..1550e8447a16 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c | |||
@@ -424,11 +424,22 @@ unsigned int irq_get_next_irq(unsigned int offset) | |||
424 | } | 424 | } |
425 | 425 | ||
426 | struct irq_desc * | 426 | 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) | ||
428 | { | 429 | { |
429 | struct irq_desc *desc = irq_to_desc(irq); | 430 | struct irq_desc *desc = irq_to_desc(irq); |
430 | 431 | ||
431 | if (desc) { | 432 | 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 | |||
432 | if (bus) | 443 | if (bus) |
433 | chip_bus_lock(desc); | 444 | chip_bus_lock(desc); |
434 | raw_spin_lock_irqsave(&desc->lock, *flags); | 445 | 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) | |||
443 | chip_bus_sync_unlock(desc); | 454 | chip_bus_sync_unlock(desc); |
444 | } | 455 | } |
445 | 456 | ||
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 | |||
446 | /** | 476 | /** |
447 | * dynamic_irq_cleanup - cleanup a dynamically allocated irq | 477 | * dynamic_irq_cleanup - cleanup a dynamically allocated irq |
448 | * @irq: irq number to initialize | 478 | * @irq: irq number to initialize |
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 7e1a3ed1e61a..7b4b156d065c 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -195,7 +195,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *mask) | |||
195 | int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m) | 195 | int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m) |
196 | { | 196 | { |
197 | unsigned long flags; | 197 | unsigned long flags; |
198 | struct irq_desc *desc = irq_get_desc_lock(irq, &flags); | 198 | struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); |
199 | 199 | ||
200 | if (!desc) | 200 | if (!desc) |
201 | return -EINVAL; | 201 | return -EINVAL; |
@@ -356,7 +356,7 @@ void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend) | |||
356 | static int __disable_irq_nosync(unsigned int irq) | 356 | static int __disable_irq_nosync(unsigned int irq) |
357 | { | 357 | { |
358 | unsigned long flags; | 358 | unsigned long flags; |
359 | struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); | 359 | struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); |
360 | 360 | ||
361 | if (!desc) | 361 | if (!desc) |
362 | return -EINVAL; | 362 | return -EINVAL; |
@@ -448,7 +448,7 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume) | |||
448 | void enable_irq(unsigned int irq) | 448 | void enable_irq(unsigned int irq) |
449 | { | 449 | { |
450 | unsigned long flags; | 450 | unsigned long flags; |
451 | struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); | 451 | struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); |
452 | 452 | ||
453 | if (!desc) | 453 | if (!desc) |
454 | return; | 454 | return; |
@@ -491,7 +491,7 @@ static int set_irq_wake_real(unsigned int irq, unsigned int on) | |||
491 | int irq_set_irq_wake(unsigned int irq, unsigned int on) | 491 | int irq_set_irq_wake(unsigned int irq, unsigned int on) |
492 | { | 492 | { |
493 | unsigned long flags; | 493 | unsigned long flags; |
494 | struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); | 494 | struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); |
495 | int ret = 0; | 495 | int ret = 0; |
496 | 496 | ||
497 | if (!desc) | 497 | if (!desc) |
@@ -532,7 +532,7 @@ EXPORT_SYMBOL(irq_set_irq_wake); | |||
532 | int can_request_irq(unsigned int irq, unsigned long irqflags) | 532 | int can_request_irq(unsigned int irq, unsigned long irqflags) |
533 | { | 533 | { |
534 | unsigned long flags; | 534 | unsigned long flags; |
535 | struct irq_desc *desc = irq_get_desc_lock(irq, &flags); | 535 | struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); |
536 | int canrequest = 0; | 536 | int canrequest = 0; |
537 | 537 | ||
538 | if (!desc) | 538 | if (!desc) |
@@ -1121,6 +1121,8 @@ int setup_irq(unsigned int irq, struct irqaction *act) | |||
1121 | int retval; | 1121 | int retval; |
1122 | struct irq_desc *desc = irq_to_desc(irq); | 1122 | struct irq_desc *desc = irq_to_desc(irq); |
1123 | 1123 | ||
1124 | if (WARN_ON(irq_settings_is_per_cpu_devid(desc))) | ||
1125 | return -EINVAL; | ||
1124 | chip_bus_lock(desc); | 1126 | chip_bus_lock(desc); |
1125 | retval = __setup_irq(irq, desc, act); | 1127 | retval = __setup_irq(irq, desc, act); |
1126 | chip_bus_sync_unlock(desc); | 1128 | chip_bus_sync_unlock(desc); |
@@ -1129,7 +1131,7 @@ int setup_irq(unsigned int irq, struct irqaction *act) | |||
1129 | } | 1131 | } |
1130 | EXPORT_SYMBOL_GPL(setup_irq); | 1132 | EXPORT_SYMBOL_GPL(setup_irq); |
1131 | 1133 | ||
1132 | /* | 1134 | /* |
1133 | * Internal function to unregister an irqaction - used to free | 1135 | * Internal function to unregister an irqaction - used to free |
1134 | * regular and special interrupts that are part of the architecture. | 1136 | * regular and special interrupts that are part of the architecture. |
1135 | */ | 1137 | */ |
@@ -1227,7 +1229,10 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id) | |||
1227 | */ | 1229 | */ |
1228 | void remove_irq(unsigned int irq, struct irqaction *act) | 1230 | void remove_irq(unsigned int irq, struct irqaction *act) |
1229 | { | 1231 | { |
1230 | __free_irq(irq, act->dev_id); | 1232 | struct irq_desc *desc = irq_to_desc(irq); |
1233 | |||
1234 | if (desc && !WARN_ON(irq_settings_is_per_cpu_devid(desc))) | ||
1235 | __free_irq(irq, act->dev_id); | ||
1231 | } | 1236 | } |
1232 | EXPORT_SYMBOL_GPL(remove_irq); | 1237 | EXPORT_SYMBOL_GPL(remove_irq); |
1233 | 1238 | ||
@@ -1249,7 +1254,7 @@ void free_irq(unsigned int irq, void *dev_id) | |||
1249 | { | 1254 | { |
1250 | struct irq_desc *desc = irq_to_desc(irq); | 1255 | struct irq_desc *desc = irq_to_desc(irq); |
1251 | 1256 | ||
1252 | if (!desc) | 1257 | if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc))) |
1253 | return; | 1258 | return; |
1254 | 1259 | ||
1255 | #ifdef CONFIG_SMP | 1260 | #ifdef CONFIG_SMP |
@@ -1327,7 +1332,8 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, | |||
1327 | if (!desc) | 1332 | if (!desc) |
1328 | return -EINVAL; | 1333 | return -EINVAL; |
1329 | 1334 | ||
1330 | if (!irq_settings_can_request(desc)) | 1335 | if (!irq_settings_can_request(desc) || |
1336 | WARN_ON(irq_settings_is_per_cpu_devid(desc))) | ||
1331 | return -EINVAL; | 1337 | return -EINVAL; |
1332 | 1338 | ||
1333 | if (!handler) { | 1339 | if (!handler) { |
@@ -1412,3 +1418,181 @@ int request_any_context_irq(unsigned int irq, irq_handler_t handler, | |||
1412 | return !ret ? IRQC_IS_HARDIRQ : ret; | 1418 | return !ret ? IRQC_IS_HARDIRQ : ret; |
1413 | } | 1419 | } |
1414 | EXPORT_SYMBOL_GPL(request_any_context_irq); | 1420 | EXPORT_SYMBOL_GPL(request_any_context_irq); |
1421 | |||
1422 | void enable_percpu_irq(unsigned int irq) | ||
1423 | { | ||
1424 | unsigned int cpu = smp_processor_id(); | ||
1425 | unsigned long flags; | ||
1426 | struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU); | ||
1427 | |||
1428 | if (!desc) | ||
1429 | return; | ||
1430 | |||
1431 | irq_percpu_enable(desc, cpu); | ||
1432 | irq_put_desc_unlock(desc, flags); | ||
1433 | } | ||
1434 | |||
1435 | void disable_percpu_irq(unsigned int irq) | ||
1436 | { | ||
1437 | unsigned int cpu = smp_processor_id(); | ||
1438 | unsigned long flags; | ||
1439 | struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU); | ||
1440 | |||
1441 | if (!desc) | ||
1442 | return; | ||
1443 | |||
1444 | irq_percpu_disable(desc, cpu); | ||
1445 | irq_put_desc_unlock(desc, flags); | ||
1446 | } | ||
1447 | |||
1448 | /* | ||
1449 | * Internal function to unregister a percpu irqaction. | ||
1450 | */ | ||
1451 | static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_id) | ||
1452 | { | ||
1453 | struct irq_desc *desc = irq_to_desc(irq); | ||
1454 | struct irqaction *action; | ||
1455 | unsigned long flags; | ||
1456 | |||
1457 | WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq); | ||
1458 | |||
1459 | if (!desc) | ||
1460 | return NULL; | ||
1461 | |||
1462 | raw_spin_lock_irqsave(&desc->lock, flags); | ||
1463 | |||
1464 | action = desc->action; | ||
1465 | if (!action || action->percpu_dev_id != dev_id) { | ||
1466 | WARN(1, "Trying to free already-free IRQ %d\n", irq); | ||
1467 | goto bad; | ||
1468 | } | ||
1469 | |||
1470 | if (!cpumask_empty(desc->percpu_enabled)) { | ||
1471 | WARN(1, "percpu IRQ %d still enabled on CPU%d!\n", | ||
1472 | irq, cpumask_first(desc->percpu_enabled)); | ||
1473 | goto bad; | ||
1474 | } | ||
1475 | |||
1476 | /* Found it - now remove it from the list of entries: */ | ||
1477 | desc->action = NULL; | ||
1478 | |||
1479 | raw_spin_unlock_irqrestore(&desc->lock, flags); | ||
1480 | |||
1481 | unregister_handler_proc(irq, action); | ||
1482 | |||
1483 | module_put(desc->owner); | ||
1484 | return action; | ||
1485 | |||
1486 | bad: | ||
1487 | raw_spin_unlock_irqrestore(&desc->lock, flags); | ||
1488 | return NULL; | ||
1489 | } | ||
1490 | |||
1491 | /** | ||
1492 | * remove_percpu_irq - free a per-cpu interrupt | ||
1493 | * @irq: Interrupt line to free | ||
1494 | * @act: irqaction for the interrupt | ||
1495 | * | ||
1496 | * Used to remove interrupts statically setup by the early boot process. | ||
1497 | */ | ||
1498 | void remove_percpu_irq(unsigned int irq, struct irqaction *act) | ||
1499 | { | ||
1500 | struct irq_desc *desc = irq_to_desc(irq); | ||
1501 | |||
1502 | if (desc && irq_settings_is_per_cpu_devid(desc)) | ||
1503 | __free_percpu_irq(irq, act->percpu_dev_id); | ||
1504 | } | ||
1505 | |||
1506 | /** | ||
1507 | * free_percpu_irq - free an interrupt allocated with request_percpu_irq | ||
1508 | * @irq: Interrupt line to free | ||
1509 | * @dev_id: Device identity to free | ||
1510 | * | ||
1511 | * Remove a percpu interrupt handler. The handler is removed, but | ||
1512 | * the interrupt line is not disabled. This must be done on each | ||
1513 | * CPU before calling this function. The function does not return | ||
1514 | * until any executing interrupts for this IRQ have completed. | ||
1515 | * | ||
1516 | * This function must not be called from interrupt context. | ||
1517 | */ | ||
1518 | void free_percpu_irq(unsigned int irq, void __percpu *dev_id) | ||
1519 | { | ||
1520 | struct irq_desc *desc = irq_to_desc(irq); | ||
1521 | |||
1522 | if (!desc || !irq_settings_is_per_cpu_devid(desc)) | ||
1523 | return; | ||
1524 | |||
1525 | chip_bus_lock(desc); | ||
1526 | kfree(__free_percpu_irq(irq, dev_id)); | ||
1527 | chip_bus_sync_unlock(desc); | ||
1528 | } | ||
1529 | |||
1530 | /** | ||
1531 | * setup_percpu_irq - setup a per-cpu interrupt | ||
1532 | * @irq: Interrupt line to setup | ||
1533 | * @act: irqaction for the interrupt | ||
1534 | * | ||
1535 | * Used to statically setup per-cpu interrupts in the early boot process. | ||
1536 | */ | ||
1537 | int setup_percpu_irq(unsigned int irq, struct irqaction *act) | ||
1538 | { | ||
1539 | struct irq_desc *desc = irq_to_desc(irq); | ||
1540 | int retval; | ||
1541 | |||
1542 | if (!desc || !irq_settings_is_per_cpu_devid(desc)) | ||
1543 | return -EINVAL; | ||
1544 | chip_bus_lock(desc); | ||
1545 | retval = __setup_irq(irq, desc, act); | ||
1546 | chip_bus_sync_unlock(desc); | ||
1547 | |||
1548 | return retval; | ||
1549 | } | ||
1550 | |||
1551 | /** | ||
1552 | * request_percpu_irq - allocate a percpu interrupt line | ||
1553 | * @irq: Interrupt line to allocate | ||
1554 | * @handler: Function to be called when the IRQ occurs. | ||
1555 | * @devname: An ascii name for the claiming device | ||
1556 | * @dev_id: A percpu cookie passed back to the handler function | ||
1557 | * | ||
1558 | * This call allocates interrupt resources, but doesn't | ||
1559 | * automatically enable the interrupt. It has to be done on each | ||
1560 | * CPU using enable_percpu_irq(). | ||
1561 | * | ||
1562 | * Dev_id must be globally unique. It is a per-cpu variable, and | ||
1563 | * the handler gets called with the interrupted CPU's instance of | ||
1564 | * that variable. | ||
1565 | */ | ||
1566 | int request_percpu_irq(unsigned int irq, irq_handler_t handler, | ||
1567 | const char *devname, void __percpu *dev_id) | ||
1568 | { | ||
1569 | struct irqaction *action; | ||
1570 | struct irq_desc *desc; | ||
1571 | int retval; | ||
1572 | |||
1573 | if (!dev_id) | ||
1574 | return -EINVAL; | ||
1575 | |||
1576 | desc = irq_to_desc(irq); | ||
1577 | if (!desc || !irq_settings_can_request(desc) || | ||
1578 | !irq_settings_is_per_cpu_devid(desc)) | ||
1579 | return -EINVAL; | ||
1580 | |||
1581 | action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); | ||
1582 | if (!action) | ||
1583 | return -ENOMEM; | ||
1584 | |||
1585 | action->handler = handler; | ||
1586 | action->flags = IRQF_PERCPU; | ||
1587 | action->name = devname; | ||
1588 | action->percpu_dev_id = dev_id; | ||
1589 | |||
1590 | chip_bus_lock(desc); | ||
1591 | retval = __setup_irq(irq, desc, action); | ||
1592 | chip_bus_sync_unlock(desc); | ||
1593 | |||
1594 | if (retval) | ||
1595 | kfree(action); | ||
1596 | |||
1597 | return retval; | ||
1598 | } | ||
diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index f1667833d444..1162f1030f18 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h | |||
@@ -13,6 +13,7 @@ enum { | |||
13 | _IRQ_MOVE_PCNTXT = IRQ_MOVE_PCNTXT, | 13 | _IRQ_MOVE_PCNTXT = IRQ_MOVE_PCNTXT, |
14 | _IRQ_NO_BALANCING = IRQ_NO_BALANCING, | 14 | _IRQ_NO_BALANCING = IRQ_NO_BALANCING, |
15 | _IRQ_NESTED_THREAD = IRQ_NESTED_THREAD, | 15 | _IRQ_NESTED_THREAD = IRQ_NESTED_THREAD, |
16 | _IRQ_PER_CPU_DEVID = IRQ_PER_CPU_DEVID, | ||
16 | _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, | 17 | _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, |
17 | }; | 18 | }; |
18 | 19 | ||
@@ -24,6 +25,7 @@ enum { | |||
24 | #define IRQ_NOTHREAD GOT_YOU_MORON | 25 | #define IRQ_NOTHREAD GOT_YOU_MORON |
25 | #define IRQ_NOAUTOEN GOT_YOU_MORON | 26 | #define IRQ_NOAUTOEN GOT_YOU_MORON |
26 | #define IRQ_NESTED_THREAD GOT_YOU_MORON | 27 | #define IRQ_NESTED_THREAD GOT_YOU_MORON |
28 | #define IRQ_PER_CPU_DEVID GOT_YOU_MORON | ||
27 | #undef IRQF_MODIFY_MASK | 29 | #undef IRQF_MODIFY_MASK |
28 | #define IRQF_MODIFY_MASK GOT_YOU_MORON | 30 | #define IRQF_MODIFY_MASK GOT_YOU_MORON |
29 | 31 | ||
@@ -39,6 +41,11 @@ static inline bool irq_settings_is_per_cpu(struct irq_desc *desc) | |||
39 | return desc->status_use_accessors & _IRQ_PER_CPU; | 41 | return desc->status_use_accessors & _IRQ_PER_CPU; |
40 | } | 42 | } |
41 | 43 | ||
44 | static inline bool irq_settings_is_per_cpu_devid(struct irq_desc *desc) | ||
45 | { | ||
46 | return desc->status_use_accessors & _IRQ_PER_CPU_DEVID; | ||
47 | } | ||
48 | |||
42 | static inline void irq_settings_set_per_cpu(struct irq_desc *desc) | 49 | static inline void irq_settings_set_per_cpu(struct irq_desc *desc) |
43 | { | 50 | { |
44 | desc->status_use_accessors |= _IRQ_PER_CPU; | 51 | desc->status_use_accessors |= _IRQ_PER_CPU; |