diff options
Diffstat (limited to 'arch/x86/kernel/irq.c')
| -rw-r--r-- | arch/x86/kernel/irq.c | 78 |
1 files changed, 73 insertions, 5 deletions
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 3973e2df7f87..b864341dcc45 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c | |||
| @@ -6,13 +6,18 @@ | |||
| 6 | #include <linux/kernel_stat.h> | 6 | #include <linux/kernel_stat.h> |
| 7 | #include <linux/seq_file.h> | 7 | #include <linux/seq_file.h> |
| 8 | #include <linux/smp.h> | 8 | #include <linux/smp.h> |
| 9 | #include <linux/ftrace.h> | ||
| 9 | 10 | ||
| 10 | #include <asm/apic.h> | 11 | #include <asm/apic.h> |
| 11 | #include <asm/io_apic.h> | 12 | #include <asm/io_apic.h> |
| 12 | #include <asm/irq.h> | 13 | #include <asm/irq.h> |
| 14 | #include <asm/idle.h> | ||
| 13 | 15 | ||
| 14 | atomic_t irq_err_count; | 16 | atomic_t irq_err_count; |
| 15 | 17 | ||
| 18 | /* Function pointer for generic interrupt vector handling */ | ||
| 19 | void (*generic_interrupt_extension)(void) = NULL; | ||
| 20 | |||
| 16 | /* | 21 | /* |
| 17 | * 'what should we do if we get a hw irq event on an illegal vector'. | 22 | * 'what should we do if we get a hw irq event on an illegal vector'. |
| 18 | * each architecture has to answer this themselves. | 23 | * each architecture has to answer this themselves. |
| @@ -36,11 +41,7 @@ void ack_bad_irq(unsigned int irq) | |||
| 36 | #endif | 41 | #endif |
| 37 | } | 42 | } |
| 38 | 43 | ||
| 39 | #ifdef CONFIG_X86_32 | 44 | #define irq_stats(x) (&per_cpu(irq_stat, x)) |
| 40 | # define irq_stats(x) (&per_cpu(irq_stat, x)) | ||
| 41 | #else | ||
| 42 | # define irq_stats(x) cpu_pda(x) | ||
| 43 | #endif | ||
| 44 | /* | 45 | /* |
| 45 | * /proc/interrupts printing: | 46 | * /proc/interrupts printing: |
| 46 | */ | 47 | */ |
| @@ -58,6 +59,12 @@ static int show_other_interrupts(struct seq_file *p) | |||
| 58 | seq_printf(p, "%10u ", irq_stats(j)->apic_timer_irqs); | 59 | seq_printf(p, "%10u ", irq_stats(j)->apic_timer_irqs); |
| 59 | seq_printf(p, " Local timer interrupts\n"); | 60 | seq_printf(p, " Local timer interrupts\n"); |
| 60 | #endif | 61 | #endif |
| 62 | if (generic_interrupt_extension) { | ||
| 63 | seq_printf(p, "PLT: "); | ||
| 64 | for_each_online_cpu(j) | ||
| 65 | seq_printf(p, "%10u ", irq_stats(j)->generic_irqs); | ||
| 66 | seq_printf(p, " Platform interrupts\n"); | ||
| 67 | } | ||
| 61 | #ifdef CONFIG_SMP | 68 | #ifdef CONFIG_SMP |
| 62 | seq_printf(p, "RES: "); | 69 | seq_printf(p, "RES: "); |
| 63 | for_each_online_cpu(j) | 70 | for_each_online_cpu(j) |
| @@ -165,6 +172,8 @@ u64 arch_irq_stat_cpu(unsigned int cpu) | |||
| 165 | #ifdef CONFIG_X86_LOCAL_APIC | 172 | #ifdef CONFIG_X86_LOCAL_APIC |
| 166 | sum += irq_stats(cpu)->apic_timer_irqs; | 173 | sum += irq_stats(cpu)->apic_timer_irqs; |
| 167 | #endif | 174 | #endif |
| 175 | if (generic_interrupt_extension) | ||
| 176 | sum += irq_stats(cpu)->generic_irqs; | ||
| 168 | #ifdef CONFIG_SMP | 177 | #ifdef CONFIG_SMP |
| 169 | sum += irq_stats(cpu)->irq_resched_count; | 178 | sum += irq_stats(cpu)->irq_resched_count; |
| 170 | sum += irq_stats(cpu)->irq_call_count; | 179 | sum += irq_stats(cpu)->irq_call_count; |
| @@ -192,4 +201,63 @@ u64 arch_irq_stat(void) | |||
| 192 | return sum; | 201 | return sum; |
| 193 | } | 202 | } |
| 194 | 203 | ||
| 204 | |||
| 205 | /* | ||
| 206 | * do_IRQ handles all normal device IRQ's (the special | ||
| 207 | * SMP cross-CPU interrupts have their own specific | ||
| 208 | * handlers). | ||
| 209 | */ | ||
| 210 | unsigned int __irq_entry do_IRQ(struct pt_regs *regs) | ||
| 211 | { | ||
| 212 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
| 213 | |||
| 214 | /* high bit used in ret_from_ code */ | ||
| 215 | unsigned vector = ~regs->orig_ax; | ||
| 216 | unsigned irq; | ||
| 217 | |||
| 218 | exit_idle(); | ||
| 219 | irq_enter(); | ||
| 220 | |||
| 221 | irq = __get_cpu_var(vector_irq)[vector]; | ||
| 222 | |||
| 223 | if (!handle_irq(irq, regs)) { | ||
| 224 | #ifdef CONFIG_X86_64 | ||
| 225 | if (!disable_apic) | ||
| 226 | ack_APIC_irq(); | ||
| 227 | #endif | ||
| 228 | |||
| 229 | if (printk_ratelimit()) | ||
| 230 | printk(KERN_EMERG "%s: %d.%d No irq handler for vector (irq %d)\n", | ||
| 231 | __func__, smp_processor_id(), vector, irq); | ||
| 232 | } | ||
| 233 | |||
| 234 | irq_exit(); | ||
| 235 | |||
| 236 | set_irq_regs(old_regs); | ||
| 237 | return 1; | ||
| 238 | } | ||
| 239 | |||
| 240 | /* | ||
| 241 | * Handler for GENERIC_INTERRUPT_VECTOR. | ||
| 242 | */ | ||
| 243 | void smp_generic_interrupt(struct pt_regs *regs) | ||
| 244 | { | ||
| 245 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
| 246 | |||
| 247 | ack_APIC_irq(); | ||
| 248 | |||
| 249 | exit_idle(); | ||
| 250 | |||
| 251 | irq_enter(); | ||
| 252 | |||
| 253 | inc_irq_stat(generic_irqs); | ||
| 254 | |||
| 255 | if (generic_interrupt_extension) | ||
| 256 | generic_interrupt_extension(); | ||
| 257 | |||
| 258 | irq_exit(); | ||
| 259 | |||
| 260 | set_irq_regs(old_regs); | ||
| 261 | } | ||
| 262 | |||
| 195 | EXPORT_SYMBOL_GPL(vector_used_by_percpu_irq); | 263 | EXPORT_SYMBOL_GPL(vector_used_by_percpu_irq); |
