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