diff options
Diffstat (limited to 'kernel/irq')
-rw-r--r-- | kernel/irq/Kconfig | 3 | ||||
-rw-r--r-- | kernel/irq/handle.c | 111 | ||||
-rw-r--r-- | kernel/irq/irqdesc.c | 40 | ||||
-rw-r--r-- | kernel/irq/migration.c | 14 |
4 files changed, 41 insertions, 127 deletions
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 31d766bf5d2e..8e42fec7686d 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig | |||
@@ -9,9 +9,6 @@ menu "IRQ subsystem" | |||
9 | config GENERIC_HARDIRQS | 9 | config GENERIC_HARDIRQS |
10 | def_bool y | 10 | def_bool y |
11 | 11 | ||
12 | config GENERIC_HARDIRQS_NO__DO_IRQ | ||
13 | def_bool y | ||
14 | |||
15 | # Select this to disable the deprecated stuff | 12 | # Select this to disable the deprecated stuff |
16 | config GENERIC_HARDIRQS_NO_DEPRECATED | 13 | config GENERIC_HARDIRQS_NO_DEPRECATED |
17 | def_bool n | 14 | def_bool n |
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index e2347eb63306..3540a7190122 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c | |||
@@ -118,114 +118,3 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) | |||
118 | 118 | ||
119 | return retval; | 119 | return retval; |
120 | } | 120 | } |
121 | |||
122 | #ifndef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ | ||
123 | |||
124 | #ifdef CONFIG_ENABLE_WARN_DEPRECATED | ||
125 | # warning __do_IRQ is deprecated. Please convert to proper flow handlers | ||
126 | #endif | ||
127 | |||
128 | /** | ||
129 | * __do_IRQ - original all in one highlevel IRQ handler | ||
130 | * @irq: the interrupt number | ||
131 | * | ||
132 | * __do_IRQ handles all normal device IRQ's (the special | ||
133 | * SMP cross-CPU interrupts have their own specific | ||
134 | * handlers). | ||
135 | * | ||
136 | * This is the original x86 implementation which is used for every | ||
137 | * interrupt type. | ||
138 | */ | ||
139 | unsigned int __do_IRQ(unsigned int irq) | ||
140 | { | ||
141 | struct irq_desc *desc = irq_to_desc(irq); | ||
142 | struct irqaction *action; | ||
143 | unsigned int status; | ||
144 | |||
145 | kstat_incr_irqs_this_cpu(irq, desc); | ||
146 | |||
147 | if (CHECK_IRQ_PER_CPU(desc->status)) { | ||
148 | irqreturn_t action_ret; | ||
149 | |||
150 | /* | ||
151 | * No locking required for CPU-local interrupts: | ||
152 | */ | ||
153 | if (desc->irq_data.chip->ack) | ||
154 | desc->irq_data.chip->ack(irq); | ||
155 | if (likely(!(desc->status & IRQ_DISABLED))) { | ||
156 | action_ret = handle_IRQ_event(irq, desc->action); | ||
157 | if (!noirqdebug) | ||
158 | note_interrupt(irq, desc, action_ret); | ||
159 | } | ||
160 | desc->irq_data.chip->end(irq); | ||
161 | return 1; | ||
162 | } | ||
163 | |||
164 | raw_spin_lock(&desc->lock); | ||
165 | if (desc->irq_data.chip->ack) | ||
166 | desc->irq_data.chip->ack(irq); | ||
167 | /* | ||
168 | * REPLAY is when Linux resends an IRQ that was dropped earlier | ||
169 | * WAITING is used by probe to mark irqs that are being tested | ||
170 | */ | ||
171 | status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); | ||
172 | status |= IRQ_PENDING; /* we _want_ to handle it */ | ||
173 | |||
174 | /* | ||
175 | * If the IRQ is disabled for whatever reason, we cannot | ||
176 | * use the action we have. | ||
177 | */ | ||
178 | action = NULL; | ||
179 | if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { | ||
180 | action = desc->action; | ||
181 | status &= ~IRQ_PENDING; /* we commit to handling */ | ||
182 | status |= IRQ_INPROGRESS; /* we are handling it */ | ||
183 | } | ||
184 | desc->status = status; | ||
185 | |||
186 | /* | ||
187 | * If there is no IRQ handler or it was disabled, exit early. | ||
188 | * Since we set PENDING, if another processor is handling | ||
189 | * a different instance of this same irq, the other processor | ||
190 | * will take care of it. | ||
191 | */ | ||
192 | if (unlikely(!action)) | ||
193 | goto out; | ||
194 | |||
195 | /* | ||
196 | * Edge triggered interrupts need to remember | ||
197 | * pending events. | ||
198 | * This applies to any hw interrupts that allow a second | ||
199 | * instance of the same irq to arrive while we are in do_IRQ | ||
200 | * or in the handler. But the code here only handles the _second_ | ||
201 | * instance of the irq, not the third or fourth. So it is mostly | ||
202 | * useful for irq hardware that does not mask cleanly in an | ||
203 | * SMP environment. | ||
204 | */ | ||
205 | for (;;) { | ||
206 | irqreturn_t action_ret; | ||
207 | |||
208 | raw_spin_unlock(&desc->lock); | ||
209 | |||
210 | action_ret = handle_IRQ_event(irq, action); | ||
211 | if (!noirqdebug) | ||
212 | note_interrupt(irq, desc, action_ret); | ||
213 | |||
214 | raw_spin_lock(&desc->lock); | ||
215 | if (likely(!(desc->status & IRQ_PENDING))) | ||
216 | break; | ||
217 | desc->status &= ~IRQ_PENDING; | ||
218 | } | ||
219 | desc->status &= ~IRQ_INPROGRESS; | ||
220 | |||
221 | out: | ||
222 | /* | ||
223 | * The ->end() handler has to deal with interrupts which got | ||
224 | * disabled while the handler was running. | ||
225 | */ | ||
226 | desc->irq_data.chip->end(irq); | ||
227 | raw_spin_unlock(&desc->lock); | ||
228 | |||
229 | return 1; | ||
230 | } | ||
231 | #endif | ||
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 9988d03797f5..282f20230e67 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c | |||
@@ -72,6 +72,8 @@ static inline int desc_node(struct irq_desc *desc) { return 0; } | |||
72 | 72 | ||
73 | static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node) | 73 | static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node) |
74 | { | 74 | { |
75 | int cpu; | ||
76 | |||
75 | desc->irq_data.irq = irq; | 77 | desc->irq_data.irq = irq; |
76 | desc->irq_data.chip = &no_irq_chip; | 78 | desc->irq_data.chip = &no_irq_chip; |
77 | desc->irq_data.chip_data = NULL; | 79 | desc->irq_data.chip_data = NULL; |
@@ -83,7 +85,8 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node) | |||
83 | desc->irq_count = 0; | 85 | desc->irq_count = 0; |
84 | desc->irqs_unhandled = 0; | 86 | desc->irqs_unhandled = 0; |
85 | desc->name = NULL; | 87 | desc->name = NULL; |
86 | memset(desc->kstat_irqs, 0, nr_cpu_ids * sizeof(*(desc->kstat_irqs))); | 88 | for_each_possible_cpu(cpu) |
89 | *per_cpu_ptr(desc->kstat_irqs, cpu) = 0; | ||
87 | desc_smp_init(desc, node); | 90 | desc_smp_init(desc, node); |
88 | } | 91 | } |
89 | 92 | ||
@@ -133,8 +136,7 @@ static struct irq_desc *alloc_desc(int irq, int node) | |||
133 | if (!desc) | 136 | if (!desc) |
134 | return NULL; | 137 | return NULL; |
135 | /* allocate based on nr_cpu_ids */ | 138 | /* allocate based on nr_cpu_ids */ |
136 | desc->kstat_irqs = kzalloc_node(nr_cpu_ids * sizeof(*desc->kstat_irqs), | 139 | desc->kstat_irqs = alloc_percpu(unsigned int); |
137 | gfp, node); | ||
138 | if (!desc->kstat_irqs) | 140 | if (!desc->kstat_irqs) |
139 | goto err_desc; | 141 | goto err_desc; |
140 | 142 | ||
@@ -149,7 +151,7 @@ static struct irq_desc *alloc_desc(int irq, int node) | |||
149 | return desc; | 151 | return desc; |
150 | 152 | ||
151 | err_kstat: | 153 | err_kstat: |
152 | kfree(desc->kstat_irqs); | 154 | free_percpu(desc->kstat_irqs); |
153 | err_desc: | 155 | err_desc: |
154 | kfree(desc); | 156 | kfree(desc); |
155 | return NULL; | 157 | return NULL; |
@@ -166,7 +168,7 @@ static void free_desc(unsigned int irq) | |||
166 | mutex_unlock(&sparse_irq_lock); | 168 | mutex_unlock(&sparse_irq_lock); |
167 | 169 | ||
168 | free_masks(desc); | 170 | free_masks(desc); |
169 | kfree(desc->kstat_irqs); | 171 | free_percpu(desc->kstat_irqs); |
170 | kfree(desc); | 172 | kfree(desc); |
171 | } | 173 | } |
172 | 174 | ||
@@ -234,7 +236,6 @@ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { | |||
234 | } | 236 | } |
235 | }; | 237 | }; |
236 | 238 | ||
237 | static unsigned int kstat_irqs_all[NR_IRQS][NR_CPUS]; | ||
238 | int __init early_irq_init(void) | 239 | int __init early_irq_init(void) |
239 | { | 240 | { |
240 | int count, i, node = first_online_node; | 241 | int count, i, node = first_online_node; |
@@ -250,7 +251,8 @@ int __init early_irq_init(void) | |||
250 | for (i = 0; i < count; i++) { | 251 | for (i = 0; i < count; i++) { |
251 | desc[i].irq_data.irq = i; | 252 | desc[i].irq_data.irq = i; |
252 | desc[i].irq_data.chip = &no_irq_chip; | 253 | desc[i].irq_data.chip = &no_irq_chip; |
253 | desc[i].kstat_irqs = kstat_irqs_all[i]; | 254 | /* TODO : do this allocation on-demand ... */ |
255 | desc[i].kstat_irqs = alloc_percpu(unsigned int); | ||
254 | alloc_masks(desc + i, GFP_KERNEL, node); | 256 | alloc_masks(desc + i, GFP_KERNEL, node); |
255 | desc_smp_init(desc + i, node); | 257 | desc_smp_init(desc + i, node); |
256 | lockdep_set_class(&desc[i].lock, &irq_desc_lock_class); | 258 | lockdep_set_class(&desc[i].lock, &irq_desc_lock_class); |
@@ -275,6 +277,22 @@ static void free_desc(unsigned int irq) | |||
275 | 277 | ||
276 | static inline int alloc_descs(unsigned int start, unsigned int cnt, int node) | 278 | static inline int alloc_descs(unsigned int start, unsigned int cnt, int node) |
277 | { | 279 | { |
280 | #if defined(CONFIG_KSTAT_IRQS_ONDEMAND) | ||
281 | struct irq_desc *desc; | ||
282 | unsigned int i; | ||
283 | |||
284 | for (i = 0; i < cnt; i++) { | ||
285 | desc = irq_to_desc(start + i); | ||
286 | if (desc && !desc->kstat_irqs) { | ||
287 | unsigned int __percpu *stats = alloc_percpu(unsigned int); | ||
288 | |||
289 | if (!stats) | ||
290 | return -1; | ||
291 | if (cmpxchg(&desc->kstat_irqs, NULL, stats) != NULL) | ||
292 | free_percpu(stats); | ||
293 | } | ||
294 | } | ||
295 | #endif | ||
278 | return start; | 296 | return start; |
279 | } | 297 | } |
280 | #endif /* !CONFIG_SPARSE_IRQ */ | 298 | #endif /* !CONFIG_SPARSE_IRQ */ |
@@ -391,7 +409,9 @@ void dynamic_irq_cleanup(unsigned int irq) | |||
391 | unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) | 409 | unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) |
392 | { | 410 | { |
393 | struct irq_desc *desc = irq_to_desc(irq); | 411 | struct irq_desc *desc = irq_to_desc(irq); |
394 | return desc ? desc->kstat_irqs[cpu] : 0; | 412 | |
413 | return desc && desc->kstat_irqs ? | ||
414 | *per_cpu_ptr(desc->kstat_irqs, cpu) : 0; | ||
395 | } | 415 | } |
396 | 416 | ||
397 | #ifdef CONFIG_GENERIC_HARDIRQS | 417 | #ifdef CONFIG_GENERIC_HARDIRQS |
@@ -401,10 +421,10 @@ unsigned int kstat_irqs(unsigned int irq) | |||
401 | int cpu; | 421 | int cpu; |
402 | int sum = 0; | 422 | int sum = 0; |
403 | 423 | ||
404 | if (!desc) | 424 | if (!desc || !desc->kstat_irqs) |
405 | return 0; | 425 | return 0; |
406 | for_each_possible_cpu(cpu) | 426 | for_each_possible_cpu(cpu) |
407 | sum += desc->kstat_irqs[cpu]; | 427 | sum += *per_cpu_ptr(desc->kstat_irqs, cpu); |
408 | return sum; | 428 | return sum; |
409 | } | 429 | } |
410 | #endif /* CONFIG_GENERIC_HARDIRQS */ | 430 | #endif /* CONFIG_GENERIC_HARDIRQS */ |
diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index 1d2541940480..441fd629ff04 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c | |||
@@ -56,6 +56,7 @@ void move_masked_irq(int irq) | |||
56 | void move_native_irq(int irq) | 56 | void move_native_irq(int irq) |
57 | { | 57 | { |
58 | struct irq_desc *desc = irq_to_desc(irq); | 58 | struct irq_desc *desc = irq_to_desc(irq); |
59 | bool masked; | ||
59 | 60 | ||
60 | if (likely(!(desc->status & IRQ_MOVE_PENDING))) | 61 | if (likely(!(desc->status & IRQ_MOVE_PENDING))) |
61 | return; | 62 | return; |
@@ -63,8 +64,15 @@ void move_native_irq(int irq) | |||
63 | if (unlikely(desc->status & IRQ_DISABLED)) | 64 | if (unlikely(desc->status & IRQ_DISABLED)) |
64 | return; | 65 | return; |
65 | 66 | ||
66 | desc->irq_data.chip->irq_mask(&desc->irq_data); | 67 | /* |
68 | * Be careful vs. already masked interrupts. If this is a | ||
69 | * threaded interrupt with ONESHOT set, we can end up with an | ||
70 | * interrupt storm. | ||
71 | */ | ||
72 | masked = desc->status & IRQ_MASKED; | ||
73 | if (!masked) | ||
74 | desc->irq_data.chip->irq_mask(&desc->irq_data); | ||
67 | move_masked_irq(irq); | 75 | move_masked_irq(irq); |
68 | desc->irq_data.chip->irq_unmask(&desc->irq_data); | 76 | if (!masked) |
77 | desc->irq_data.chip->irq_unmask(&desc->irq_data); | ||
69 | } | 78 | } |
70 | |||