diff options
Diffstat (limited to 'kernel/irq/handle.c')
-rw-r--r-- | kernel/irq/handle.c | 205 |
1 files changed, 195 insertions, 10 deletions
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index c815b42d0f5b..c20db0be9173 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c | |||
@@ -15,9 +15,16 @@ | |||
15 | #include <linux/random.h> | 15 | #include <linux/random.h> |
16 | #include <linux/interrupt.h> | 16 | #include <linux/interrupt.h> |
17 | #include <linux/kernel_stat.h> | 17 | #include <linux/kernel_stat.h> |
18 | #include <linux/rculist.h> | ||
19 | #include <linux/hash.h> | ||
18 | 20 | ||
19 | #include "internals.h" | 21 | #include "internals.h" |
20 | 22 | ||
23 | /* | ||
24 | * lockdep: we want to handle all irq_desc locks as a single lock-class: | ||
25 | */ | ||
26 | struct lock_class_key irq_desc_lock_class; | ||
27 | |||
21 | /** | 28 | /** |
22 | * handle_bad_irq - handle spurious and unhandled irqs | 29 | * handle_bad_irq - handle spurious and unhandled irqs |
23 | * @irq: the interrupt number | 30 | * @irq: the interrupt number |
@@ -49,6 +56,150 @@ void handle_bad_irq(unsigned int irq, struct irq_desc *desc) | |||
49 | int nr_irqs = NR_IRQS; | 56 | int nr_irqs = NR_IRQS; |
50 | EXPORT_SYMBOL_GPL(nr_irqs); | 57 | EXPORT_SYMBOL_GPL(nr_irqs); |
51 | 58 | ||
59 | #ifdef CONFIG_SPARSE_IRQ | ||
60 | static struct irq_desc irq_desc_init = { | ||
61 | .irq = -1, | ||
62 | .status = IRQ_DISABLED, | ||
63 | .chip = &no_irq_chip, | ||
64 | .handle_irq = handle_bad_irq, | ||
65 | .depth = 1, | ||
66 | .lock = __SPIN_LOCK_UNLOCKED(irq_desc_init.lock), | ||
67 | #ifdef CONFIG_SMP | ||
68 | .affinity = CPU_MASK_ALL | ||
69 | #endif | ||
70 | }; | ||
71 | |||
72 | void init_kstat_irqs(struct irq_desc *desc, int cpu, int nr) | ||
73 | { | ||
74 | unsigned long bytes; | ||
75 | char *ptr; | ||
76 | int node; | ||
77 | |||
78 | /* Compute how many bytes we need per irq and allocate them */ | ||
79 | bytes = nr * sizeof(unsigned int); | ||
80 | |||
81 | node = cpu_to_node(cpu); | ||
82 | ptr = kzalloc_node(bytes, GFP_ATOMIC, node); | ||
83 | printk(KERN_DEBUG " alloc kstat_irqs on cpu %d node %d\n", cpu, node); | ||
84 | |||
85 | if (ptr) | ||
86 | desc->kstat_irqs = (unsigned int *)ptr; | ||
87 | } | ||
88 | |||
89 | static void init_one_irq_desc(int irq, struct irq_desc *desc, int cpu) | ||
90 | { | ||
91 | memcpy(desc, &irq_desc_init, sizeof(struct irq_desc)); | ||
92 | |||
93 | spin_lock_init(&desc->lock); | ||
94 | desc->irq = irq; | ||
95 | #ifdef CONFIG_SMP | ||
96 | desc->cpu = cpu; | ||
97 | #endif | ||
98 | lockdep_set_class(&desc->lock, &irq_desc_lock_class); | ||
99 | init_kstat_irqs(desc, cpu, nr_cpu_ids); | ||
100 | if (!desc->kstat_irqs) { | ||
101 | printk(KERN_ERR "can not alloc kstat_irqs\n"); | ||
102 | BUG_ON(1); | ||
103 | } | ||
104 | arch_init_chip_data(desc, cpu); | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * Protect the sparse_irqs: | ||
109 | */ | ||
110 | DEFINE_SPINLOCK(sparse_irq_lock); | ||
111 | |||
112 | struct irq_desc *irq_desc_ptrs[NR_IRQS] __read_mostly; | ||
113 | |||
114 | static struct irq_desc irq_desc_legacy[NR_IRQS_LEGACY] __cacheline_aligned_in_smp = { | ||
115 | [0 ... NR_IRQS_LEGACY-1] = { | ||
116 | .irq = -1, | ||
117 | .status = IRQ_DISABLED, | ||
118 | .chip = &no_irq_chip, | ||
119 | .handle_irq = handle_bad_irq, | ||
120 | .depth = 1, | ||
121 | .lock = __SPIN_LOCK_UNLOCKED(irq_desc_init.lock), | ||
122 | #ifdef CONFIG_SMP | ||
123 | .affinity = CPU_MASK_ALL | ||
124 | #endif | ||
125 | } | ||
126 | }; | ||
127 | |||
128 | /* FIXME: use bootmem alloc ...*/ | ||
129 | static unsigned int kstat_irqs_legacy[NR_IRQS_LEGACY][NR_CPUS]; | ||
130 | |||
131 | int __init early_irq_init(void) | ||
132 | { | ||
133 | struct irq_desc *desc; | ||
134 | int legacy_count; | ||
135 | int i; | ||
136 | |||
137 | desc = irq_desc_legacy; | ||
138 | legacy_count = ARRAY_SIZE(irq_desc_legacy); | ||
139 | |||
140 | for (i = 0; i < legacy_count; i++) { | ||
141 | desc[i].irq = i; | ||
142 | desc[i].kstat_irqs = kstat_irqs_legacy[i]; | ||
143 | lockdep_set_class(&desc[i].lock, &irq_desc_lock_class); | ||
144 | |||
145 | irq_desc_ptrs[i] = desc + i; | ||
146 | } | ||
147 | |||
148 | for (i = legacy_count; i < NR_IRQS; i++) | ||
149 | irq_desc_ptrs[i] = NULL; | ||
150 | |||
151 | return arch_early_irq_init(); | ||
152 | } | ||
153 | |||
154 | struct irq_desc *irq_to_desc(unsigned int irq) | ||
155 | { | ||
156 | return (irq < NR_IRQS) ? irq_desc_ptrs[irq] : NULL; | ||
157 | } | ||
158 | |||
159 | struct irq_desc *irq_to_desc_alloc_cpu(unsigned int irq, int cpu) | ||
160 | { | ||
161 | struct irq_desc *desc; | ||
162 | unsigned long flags; | ||
163 | int node; | ||
164 | |||
165 | if (irq >= NR_IRQS) { | ||
166 | printk(KERN_WARNING "irq >= NR_IRQS in irq_to_desc_alloc: %d %d\n", | ||
167 | irq, NR_IRQS); | ||
168 | WARN_ON(1); | ||
169 | return NULL; | ||
170 | } | ||
171 | |||
172 | desc = irq_desc_ptrs[irq]; | ||
173 | if (desc) | ||
174 | return desc; | ||
175 | |||
176 | spin_lock_irqsave(&sparse_irq_lock, flags); | ||
177 | |||
178 | /* We have to check it to avoid races with another CPU */ | ||
179 | desc = irq_desc_ptrs[irq]; | ||
180 | if (desc) | ||
181 | goto out_unlock; | ||
182 | |||
183 | node = cpu_to_node(cpu); | ||
184 | desc = kzalloc_node(sizeof(*desc), GFP_ATOMIC, node); | ||
185 | printk(KERN_DEBUG " alloc irq_desc for %d on cpu %d node %d\n", | ||
186 | irq, cpu, node); | ||
187 | if (!desc) { | ||
188 | printk(KERN_ERR "can not alloc irq_desc\n"); | ||
189 | BUG_ON(1); | ||
190 | } | ||
191 | init_one_irq_desc(irq, desc, cpu); | ||
192 | |||
193 | irq_desc_ptrs[irq] = desc; | ||
194 | |||
195 | out_unlock: | ||
196 | spin_unlock_irqrestore(&sparse_irq_lock, flags); | ||
197 | |||
198 | return desc; | ||
199 | } | ||
200 | |||
201 | #else /* !CONFIG_SPARSE_IRQ */ | ||
202 | |||
52 | struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { | 203 | struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { |
53 | [0 ... NR_IRQS-1] = { | 204 | [0 ... NR_IRQS-1] = { |
54 | .status = IRQ_DISABLED, | 205 | .status = IRQ_DISABLED, |
@@ -62,6 +213,32 @@ struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { | |||
62 | } | 213 | } |
63 | }; | 214 | }; |
64 | 215 | ||
216 | int __init early_irq_init(void) | ||
217 | { | ||
218 | struct irq_desc *desc; | ||
219 | int count; | ||
220 | int i; | ||
221 | |||
222 | desc = irq_desc; | ||
223 | count = ARRAY_SIZE(irq_desc); | ||
224 | |||
225 | for (i = 0; i < count; i++) | ||
226 | desc[i].irq = i; | ||
227 | |||
228 | return arch_early_irq_init(); | ||
229 | } | ||
230 | |||
231 | struct irq_desc *irq_to_desc(unsigned int irq) | ||
232 | { | ||
233 | return (irq < NR_IRQS) ? irq_desc + irq : NULL; | ||
234 | } | ||
235 | |||
236 | struct irq_desc *irq_to_desc_alloc_cpu(unsigned int irq, int cpu) | ||
237 | { | ||
238 | return irq_to_desc(irq); | ||
239 | } | ||
240 | #endif /* !CONFIG_SPARSE_IRQ */ | ||
241 | |||
65 | /* | 242 | /* |
66 | * What should we do if we get a hw irq event on an illegal vector? | 243 | * What should we do if we get a hw irq event on an illegal vector? |
67 | * Each architecture has to answer this themself. | 244 | * Each architecture has to answer this themself. |
@@ -179,8 +356,11 @@ unsigned int __do_IRQ(unsigned int irq) | |||
179 | /* | 356 | /* |
180 | * No locking required for CPU-local interrupts: | 357 | * No locking required for CPU-local interrupts: |
181 | */ | 358 | */ |
182 | if (desc->chip->ack) | 359 | if (desc->chip->ack) { |
183 | desc->chip->ack(irq); | 360 | desc->chip->ack(irq); |
361 | /* get new one */ | ||
362 | desc = irq_remap_to_desc(irq, desc); | ||
363 | } | ||
184 | if (likely(!(desc->status & IRQ_DISABLED))) { | 364 | if (likely(!(desc->status & IRQ_DISABLED))) { |
185 | action_ret = handle_IRQ_event(irq, desc->action); | 365 | action_ret = handle_IRQ_event(irq, desc->action); |
186 | if (!noirqdebug) | 366 | if (!noirqdebug) |
@@ -191,8 +371,10 @@ unsigned int __do_IRQ(unsigned int irq) | |||
191 | } | 371 | } |
192 | 372 | ||
193 | spin_lock(&desc->lock); | 373 | spin_lock(&desc->lock); |
194 | if (desc->chip->ack) | 374 | if (desc->chip->ack) { |
195 | desc->chip->ack(irq); | 375 | desc->chip->ack(irq); |
376 | desc = irq_remap_to_desc(irq, desc); | ||
377 | } | ||
196 | /* | 378 | /* |
197 | * REPLAY is when Linux resends an IRQ that was dropped earlier | 379 | * REPLAY is when Linux resends an IRQ that was dropped earlier |
198 | * WAITING is used by probe to mark irqs that are being tested | 380 | * WAITING is used by probe to mark irqs that are being tested |
@@ -259,19 +441,22 @@ out: | |||
259 | } | 441 | } |
260 | #endif | 442 | #endif |
261 | 443 | ||
262 | |||
263 | #ifdef CONFIG_TRACE_IRQFLAGS | ||
264 | /* | ||
265 | * lockdep: we want to handle all irq_desc locks as a single lock-class: | ||
266 | */ | ||
267 | static struct lock_class_key irq_desc_lock_class; | ||
268 | |||
269 | void early_init_irq_lock_class(void) | 444 | void early_init_irq_lock_class(void) |
270 | { | 445 | { |
271 | struct irq_desc *desc; | 446 | struct irq_desc *desc; |
272 | int i; | 447 | int i; |
273 | 448 | ||
274 | for_each_irq_desc(i, desc) | 449 | for_each_irq_desc(i, desc) { |
275 | lockdep_set_class(&desc->lock, &irq_desc_lock_class); | 450 | lockdep_set_class(&desc->lock, &irq_desc_lock_class); |
451 | } | ||
452 | } | ||
453 | |||
454 | #ifdef CONFIG_SPARSE_IRQ | ||
455 | unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) | ||
456 | { | ||
457 | struct irq_desc *desc = irq_to_desc(irq); | ||
458 | return desc ? desc->kstat_irqs[cpu] : 0; | ||
276 | } | 459 | } |
277 | #endif | 460 | #endif |
461 | EXPORT_SYMBOL(kstat_irqs_cpu); | ||
462 | |||