diff options
Diffstat (limited to 'arch/x86/kernel/irq_32.c')
-rw-r--r-- | arch/x86/kernel/irq_32.c | 253 |
1 files changed, 155 insertions, 98 deletions
diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c index 147352df28b..1cf8c1fcc08 100644 --- a/arch/x86/kernel/irq_32.c +++ b/arch/x86/kernel/irq_32.c | |||
@@ -48,6 +48,29 @@ void ack_bad_irq(unsigned int irq) | |||
48 | #endif | 48 | #endif |
49 | } | 49 | } |
50 | 50 | ||
51 | #ifdef CONFIG_DEBUG_STACKOVERFLOW | ||
52 | /* Debugging check for stack overflow: is there less than 1KB free? */ | ||
53 | static int check_stack_overflow(void) | ||
54 | { | ||
55 | long sp; | ||
56 | |||
57 | __asm__ __volatile__("andl %%esp,%0" : | ||
58 | "=r" (sp) : "0" (THREAD_SIZE - 1)); | ||
59 | |||
60 | return sp < (sizeof(struct thread_info) + STACK_WARN); | ||
61 | } | ||
62 | |||
63 | static void print_stack_overflow(void) | ||
64 | { | ||
65 | printk(KERN_WARNING "low stack detected by irq handler\n"); | ||
66 | dump_stack(); | ||
67 | } | ||
68 | |||
69 | #else | ||
70 | static inline int check_stack_overflow(void) { return 0; } | ||
71 | static inline void print_stack_overflow(void) { } | ||
72 | #endif | ||
73 | |||
51 | #ifdef CONFIG_4KSTACKS | 74 | #ifdef CONFIG_4KSTACKS |
52 | /* | 75 | /* |
53 | * per-CPU IRQ handling contexts (thread information and stack) | 76 | * per-CPU IRQ handling contexts (thread information and stack) |
@@ -59,48 +82,26 @@ union irq_ctx { | |||
59 | 82 | ||
60 | static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly; | 83 | static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly; |
61 | static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly; | 84 | static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly; |
62 | #endif | ||
63 | |||
64 | /* | ||
65 | * do_IRQ handles all normal device IRQ's (the special | ||
66 | * SMP cross-CPU interrupts have their own specific | ||
67 | * handlers). | ||
68 | */ | ||
69 | unsigned int do_IRQ(struct pt_regs *regs) | ||
70 | { | ||
71 | struct pt_regs *old_regs; | ||
72 | /* high bit used in ret_from_ code */ | ||
73 | int irq = ~regs->orig_ax; | ||
74 | struct irq_desc *desc = irq_desc + irq; | ||
75 | #ifdef CONFIG_4KSTACKS | ||
76 | union irq_ctx *curctx, *irqctx; | ||
77 | u32 *isp; | ||
78 | #endif | ||
79 | 85 | ||
80 | if (unlikely((unsigned)irq >= NR_IRQS)) { | 86 | static char softirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; |
81 | printk(KERN_EMERG "%s: cannot handle IRQ %d\n", | 87 | static char hardirq_stack[NR_CPUS * THREAD_SIZE] __page_aligned_bss; |
82 | __func__, irq); | ||
83 | BUG(); | ||
84 | } | ||
85 | 88 | ||
86 | old_regs = set_irq_regs(regs); | 89 | static void call_on_stack(void *func, void *stack) |
87 | irq_enter(); | 90 | { |
88 | #ifdef CONFIG_DEBUG_STACKOVERFLOW | 91 | asm volatile("xchgl %%ebx,%%esp \n" |
89 | /* Debugging check for stack overflow: is there less than 1KB free? */ | 92 | "call *%%edi \n" |
90 | { | 93 | "movl %%ebx,%%esp \n" |
91 | long sp; | 94 | : "=b" (stack) |
92 | 95 | : "0" (stack), | |
93 | __asm__ __volatile__("andl %%esp,%0" : | 96 | "D"(func) |
94 | "=r" (sp) : "0" (THREAD_SIZE - 1)); | 97 | : "memory", "cc", "edx", "ecx", "eax"); |
95 | if (unlikely(sp < (sizeof(struct thread_info) + STACK_WARN))) { | 98 | } |
96 | printk("do_IRQ: stack overflow: %ld\n", | ||
97 | sp - sizeof(struct thread_info)); | ||
98 | dump_stack(); | ||
99 | } | ||
100 | } | ||
101 | #endif | ||
102 | 99 | ||
103 | #ifdef CONFIG_4KSTACKS | 100 | static inline int |
101 | execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq) | ||
102 | { | ||
103 | union irq_ctx *curctx, *irqctx; | ||
104 | u32 *isp, arg1, arg2; | ||
104 | 105 | ||
105 | curctx = (union irq_ctx *) current_thread_info(); | 106 | curctx = (union irq_ctx *) current_thread_info(); |
106 | irqctx = hardirq_ctx[smp_processor_id()]; | 107 | irqctx = hardirq_ctx[smp_processor_id()]; |
@@ -111,52 +112,39 @@ unsigned int do_IRQ(struct pt_regs *regs) | |||
111 | * handler) we can't do that and just have to keep using the | 112 | * handler) we can't do that and just have to keep using the |
112 | * current stack (which is the irq stack already after all) | 113 | * current stack (which is the irq stack already after all) |
113 | */ | 114 | */ |
114 | if (curctx != irqctx) { | 115 | if (unlikely(curctx == irqctx)) |
115 | int arg1, arg2, bx; | 116 | return 0; |
116 | |||
117 | /* build the stack frame on the IRQ stack */ | ||
118 | isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); | ||
119 | irqctx->tinfo.task = curctx->tinfo.task; | ||
120 | irqctx->tinfo.previous_esp = current_stack_pointer; | ||
121 | 117 | ||
122 | /* | 118 | /* build the stack frame on the IRQ stack */ |
123 | * Copy the softirq bits in preempt_count so that the | 119 | isp = (u32 *) ((char*)irqctx + sizeof(*irqctx)); |
124 | * softirq checks work in the hardirq context. | 120 | irqctx->tinfo.task = curctx->tinfo.task; |
125 | */ | 121 | irqctx->tinfo.previous_esp = current_stack_pointer; |
126 | irqctx->tinfo.preempt_count = | ||
127 | (irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) | | ||
128 | (curctx->tinfo.preempt_count & SOFTIRQ_MASK); | ||
129 | |||
130 | asm volatile( | ||
131 | " xchgl %%ebx,%%esp \n" | ||
132 | " call *%%edi \n" | ||
133 | " movl %%ebx,%%esp \n" | ||
134 | : "=a" (arg1), "=d" (arg2), "=b" (bx) | ||
135 | : "0" (irq), "1" (desc), "2" (isp), | ||
136 | "D" (desc->handle_irq) | ||
137 | : "memory", "cc", "ecx" | ||
138 | ); | ||
139 | } else | ||
140 | #endif | ||
141 | desc->handle_irq(irq, desc); | ||
142 | 122 | ||
143 | irq_exit(); | 123 | /* |
144 | set_irq_regs(old_regs); | 124 | * Copy the softirq bits in preempt_count so that the |
125 | * softirq checks work in the hardirq context. | ||
126 | */ | ||
127 | irqctx->tinfo.preempt_count = | ||
128 | (irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) | | ||
129 | (curctx->tinfo.preempt_count & SOFTIRQ_MASK); | ||
130 | |||
131 | if (unlikely(overflow)) | ||
132 | call_on_stack(print_stack_overflow, isp); | ||
133 | |||
134 | asm volatile("xchgl %%ebx,%%esp \n" | ||
135 | "call *%%edi \n" | ||
136 | "movl %%ebx,%%esp \n" | ||
137 | : "=a" (arg1), "=d" (arg2), "=b" (isp) | ||
138 | : "0" (irq), "1" (desc), "2" (isp), | ||
139 | "D" (desc->handle_irq) | ||
140 | : "memory", "cc", "ecx"); | ||
145 | return 1; | 141 | return 1; |
146 | } | 142 | } |
147 | 143 | ||
148 | #ifdef CONFIG_4KSTACKS | ||
149 | |||
150 | static char softirq_stack[NR_CPUS * THREAD_SIZE] | ||
151 | __attribute__((__section__(".bss.page_aligned"))); | ||
152 | |||
153 | static char hardirq_stack[NR_CPUS * THREAD_SIZE] | ||
154 | __attribute__((__section__(".bss.page_aligned"))); | ||
155 | |||
156 | /* | 144 | /* |
157 | * allocate per-cpu stacks for hardirq and for softirq processing | 145 | * allocate per-cpu stacks for hardirq and for softirq processing |
158 | */ | 146 | */ |
159 | void irq_ctx_init(int cpu) | 147 | void __cpuinit irq_ctx_init(int cpu) |
160 | { | 148 | { |
161 | union irq_ctx *irqctx; | 149 | union irq_ctx *irqctx; |
162 | 150 | ||
@@ -164,25 +152,25 @@ void irq_ctx_init(int cpu) | |||
164 | return; | 152 | return; |
165 | 153 | ||
166 | irqctx = (union irq_ctx*) &hardirq_stack[cpu*THREAD_SIZE]; | 154 | irqctx = (union irq_ctx*) &hardirq_stack[cpu*THREAD_SIZE]; |
167 | irqctx->tinfo.task = NULL; | 155 | irqctx->tinfo.task = NULL; |
168 | irqctx->tinfo.exec_domain = NULL; | 156 | irqctx->tinfo.exec_domain = NULL; |
169 | irqctx->tinfo.cpu = cpu; | 157 | irqctx->tinfo.cpu = cpu; |
170 | irqctx->tinfo.preempt_count = HARDIRQ_OFFSET; | 158 | irqctx->tinfo.preempt_count = HARDIRQ_OFFSET; |
171 | irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); | 159 | irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); |
172 | 160 | ||
173 | hardirq_ctx[cpu] = irqctx; | 161 | hardirq_ctx[cpu] = irqctx; |
174 | 162 | ||
175 | irqctx = (union irq_ctx*) &softirq_stack[cpu*THREAD_SIZE]; | 163 | irqctx = (union irq_ctx*) &softirq_stack[cpu*THREAD_SIZE]; |
176 | irqctx->tinfo.task = NULL; | 164 | irqctx->tinfo.task = NULL; |
177 | irqctx->tinfo.exec_domain = NULL; | 165 | irqctx->tinfo.exec_domain = NULL; |
178 | irqctx->tinfo.cpu = cpu; | 166 | irqctx->tinfo.cpu = cpu; |
179 | irqctx->tinfo.preempt_count = 0; | 167 | irqctx->tinfo.preempt_count = 0; |
180 | irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); | 168 | irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); |
181 | 169 | ||
182 | softirq_ctx[cpu] = irqctx; | 170 | softirq_ctx[cpu] = irqctx; |
183 | 171 | ||
184 | printk("CPU %u irqstacks, hard=%p soft=%p\n", | 172 | printk(KERN_DEBUG "CPU %u irqstacks, hard=%p soft=%p\n", |
185 | cpu,hardirq_ctx[cpu],softirq_ctx[cpu]); | 173 | cpu,hardirq_ctx[cpu],softirq_ctx[cpu]); |
186 | } | 174 | } |
187 | 175 | ||
188 | void irq_ctx_exit(int cpu) | 176 | void irq_ctx_exit(int cpu) |
@@ -211,25 +199,56 @@ asmlinkage void do_softirq(void) | |||
211 | /* build the stack frame on the softirq stack */ | 199 | /* build the stack frame on the softirq stack */ |
212 | isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); | 200 | isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); |
213 | 201 | ||
214 | asm volatile( | 202 | call_on_stack(__do_softirq, isp); |
215 | " xchgl %%ebx,%%esp \n" | ||
216 | " call __do_softirq \n" | ||
217 | " movl %%ebx,%%esp \n" | ||
218 | : "=b"(isp) | ||
219 | : "0"(isp) | ||
220 | : "memory", "cc", "edx", "ecx", "eax" | ||
221 | ); | ||
222 | /* | 203 | /* |
223 | * Shouldnt happen, we returned above if in_interrupt(): | 204 | * Shouldnt happen, we returned above if in_interrupt(): |
224 | */ | 205 | */ |
225 | WARN_ON_ONCE(softirq_count()); | 206 | WARN_ON_ONCE(softirq_count()); |
226 | } | 207 | } |
227 | 208 | ||
228 | local_irq_restore(flags); | 209 | local_irq_restore(flags); |
229 | } | 210 | } |
211 | |||
212 | #else | ||
213 | static inline int | ||
214 | execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq) { return 0; } | ||
230 | #endif | 215 | #endif |
231 | 216 | ||
232 | /* | 217 | /* |
218 | * do_IRQ handles all normal device IRQ's (the special | ||
219 | * SMP cross-CPU interrupts have their own specific | ||
220 | * handlers). | ||
221 | */ | ||
222 | unsigned int do_IRQ(struct pt_regs *regs) | ||
223 | { | ||
224 | struct pt_regs *old_regs; | ||
225 | /* high bit used in ret_from_ code */ | ||
226 | int overflow, irq = ~regs->orig_ax; | ||
227 | struct irq_desc *desc = irq_desc + irq; | ||
228 | |||
229 | if (unlikely((unsigned)irq >= NR_IRQS)) { | ||
230 | printk(KERN_EMERG "%s: cannot handle IRQ %d\n", | ||
231 | __func__, irq); | ||
232 | BUG(); | ||
233 | } | ||
234 | |||
235 | old_regs = set_irq_regs(regs); | ||
236 | irq_enter(); | ||
237 | |||
238 | overflow = check_stack_overflow(); | ||
239 | |||
240 | if (!execute_on_irq_stack(overflow, desc, irq)) { | ||
241 | if (unlikely(overflow)) | ||
242 | print_stack_overflow(); | ||
243 | desc->handle_irq(irq, desc); | ||
244 | } | ||
245 | |||
246 | irq_exit(); | ||
247 | set_irq_regs(old_regs); | ||
248 | return 1; | ||
249 | } | ||
250 | |||
251 | /* | ||
233 | * Interrupt statistics: | 252 | * Interrupt statistics: |
234 | */ | 253 | */ |
235 | 254 | ||
@@ -313,16 +332,20 @@ skip: | |||
313 | per_cpu(irq_stat,j).irq_tlb_count); | 332 | per_cpu(irq_stat,j).irq_tlb_count); |
314 | seq_printf(p, " TLB shootdowns\n"); | 333 | seq_printf(p, " TLB shootdowns\n"); |
315 | #endif | 334 | #endif |
335 | #ifdef CONFIG_X86_MCE | ||
316 | seq_printf(p, "TRM: "); | 336 | seq_printf(p, "TRM: "); |
317 | for_each_online_cpu(j) | 337 | for_each_online_cpu(j) |
318 | seq_printf(p, "%10u ", | 338 | seq_printf(p, "%10u ", |
319 | per_cpu(irq_stat,j).irq_thermal_count); | 339 | per_cpu(irq_stat,j).irq_thermal_count); |
320 | seq_printf(p, " Thermal event interrupts\n"); | 340 | seq_printf(p, " Thermal event interrupts\n"); |
341 | #endif | ||
342 | #ifdef CONFIG_X86_LOCAL_APIC | ||
321 | seq_printf(p, "SPU: "); | 343 | seq_printf(p, "SPU: "); |
322 | for_each_online_cpu(j) | 344 | for_each_online_cpu(j) |
323 | seq_printf(p, "%10u ", | 345 | seq_printf(p, "%10u ", |
324 | per_cpu(irq_stat,j).irq_spurious_count); | 346 | per_cpu(irq_stat,j).irq_spurious_count); |
325 | seq_printf(p, " Spurious interrupts\n"); | 347 | seq_printf(p, " Spurious interrupts\n"); |
348 | #endif | ||
326 | seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); | 349 | seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); |
327 | #if defined(CONFIG_X86_IO_APIC) | 350 | #if defined(CONFIG_X86_IO_APIC) |
328 | seq_printf(p, "MIS: %10u\n", atomic_read(&irq_mis_count)); | 351 | seq_printf(p, "MIS: %10u\n", atomic_read(&irq_mis_count)); |
@@ -331,6 +354,40 @@ skip: | |||
331 | return 0; | 354 | return 0; |
332 | } | 355 | } |
333 | 356 | ||
357 | /* | ||
358 | * /proc/stat helpers | ||
359 | */ | ||
360 | u64 arch_irq_stat_cpu(unsigned int cpu) | ||
361 | { | ||
362 | u64 sum = nmi_count(cpu); | ||
363 | |||
364 | #ifdef CONFIG_X86_LOCAL_APIC | ||
365 | sum += per_cpu(irq_stat, cpu).apic_timer_irqs; | ||
366 | #endif | ||
367 | #ifdef CONFIG_SMP | ||
368 | sum += per_cpu(irq_stat, cpu).irq_resched_count; | ||
369 | sum += per_cpu(irq_stat, cpu).irq_call_count; | ||
370 | sum += per_cpu(irq_stat, cpu).irq_tlb_count; | ||
371 | #endif | ||
372 | #ifdef CONFIG_X86_MCE | ||
373 | sum += per_cpu(irq_stat, cpu).irq_thermal_count; | ||
374 | #endif | ||
375 | #ifdef CONFIG_X86_LOCAL_APIC | ||
376 | sum += per_cpu(irq_stat, cpu).irq_spurious_count; | ||
377 | #endif | ||
378 | return sum; | ||
379 | } | ||
380 | |||
381 | u64 arch_irq_stat(void) | ||
382 | { | ||
383 | u64 sum = atomic_read(&irq_err_count); | ||
384 | |||
385 | #ifdef CONFIG_X86_IO_APIC | ||
386 | sum += atomic_read(&irq_mis_count); | ||
387 | #endif | ||
388 | return sum; | ||
389 | } | ||
390 | |||
334 | #ifdef CONFIG_HOTPLUG_CPU | 391 | #ifdef CONFIG_HOTPLUG_CPU |
335 | #include <mach_apic.h> | 392 | #include <mach_apic.h> |
336 | 393 | ||