Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/x86_64/kernel/irq.c | |
3 | * | |
4 | * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar | |
5 | * | |
6 | * This file contains the lowest level x86_64-specific interrupt | |
7 | * entry and irq statistics code. All the remaining irq logic is | |
8 | * done by the generic kernel/irq/ code and in the | |
9 | * x86_64-specific irq controller code. (e.g. i8259.c and | |
10 | * io_apic.c.) | |
11 | */ | |
12 | ||
13 | #include <linux/kernel_stat.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/seq_file.h> | |
16 | #include <linux/module.h> | |
76e4f660 | 17 | #include <linux/delay.h> |
1da177e4 LT |
18 | #include <asm/uaccess.h> |
19 | #include <asm/io_apic.h> | |
95833c83 | 20 | #include <asm/idle.h> |
1da177e4 LT |
21 | |
22 | atomic_t irq_err_count; | |
23 | #ifdef CONFIG_X86_IO_APIC | |
24 | #ifdef APIC_MISMATCH_DEBUG | |
25 | atomic_t irq_mis_count; | |
26 | #endif | |
27 | #endif | |
28 | ||
4961f10e ES |
29 | #ifdef CONFIG_DEBUG_STACKOVERFLOW |
30 | /* | |
31 | * Probabilistic stack overflow check: | |
32 | * | |
33 | * Only check the stack in process context, because everything else | |
34 | * runs on the big interrupt stacks. Checking reliably is too expensive, | |
35 | * so we just check from interrupts. | |
36 | */ | |
37 | static inline void stack_overflow_check(struct pt_regs *regs) | |
38 | { | |
39 | u64 curbase = (u64) current->thread_info; | |
40 | static unsigned long warned = -60*HZ; | |
41 | ||
42 | if (regs->rsp >= curbase && regs->rsp <= curbase + THREAD_SIZE && | |
43 | regs->rsp < curbase + sizeof(struct thread_info) + 128 && | |
44 | time_after(jiffies, warned + 60*HZ)) { | |
45 | printk("do_IRQ: %s near stack overflow (cur:%Lx,rsp:%lx)\n", | |
46 | current->comm, curbase, regs->rsp); | |
47 | show_stack(NULL,NULL); | |
48 | warned = jiffies; | |
49 | } | |
50 | } | |
51 | #endif | |
52 | ||
1da177e4 LT |
53 | /* |
54 | * Generic, controller-independent functions: | |
55 | */ | |
56 | ||
57 | int show_interrupts(struct seq_file *p, void *v) | |
58 | { | |
59 | int i = *(loff_t *) v, j; | |
60 | struct irqaction * action; | |
61 | unsigned long flags; | |
62 | ||
63 | if (i == 0) { | |
64 | seq_printf(p, " "); | |
394e3902 | 65 | for_each_online_cpu(j) |
bdbdaa79 | 66 | seq_printf(p, "CPU%-8d",j); |
1da177e4 LT |
67 | seq_putc(p, '\n'); |
68 | } | |
69 | ||
70 | if (i < NR_IRQS) { | |
71 | spin_lock_irqsave(&irq_desc[i].lock, flags); | |
72 | action = irq_desc[i].action; | |
73 | if (!action) | |
74 | goto skip; | |
75 | seq_printf(p, "%3d: ",i); | |
76 | #ifndef CONFIG_SMP | |
77 | seq_printf(p, "%10u ", kstat_irqs(i)); | |
78 | #else | |
394e3902 AM |
79 | for_each_online_cpu(j) |
80 | seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); | |
1da177e4 LT |
81 | #endif |
82 | seq_printf(p, " %14s", irq_desc[i].handler->typename); | |
83 | ||
84 | seq_printf(p, " %s", action->name); | |
85 | for (action=action->next; action; action = action->next) | |
86 | seq_printf(p, ", %s", action->name); | |
87 | seq_putc(p, '\n'); | |
88 | skip: | |
89 | spin_unlock_irqrestore(&irq_desc[i].lock, flags); | |
90 | } else if (i == NR_IRQS) { | |
91 | seq_printf(p, "NMI: "); | |
394e3902 AM |
92 | for_each_online_cpu(j) |
93 | seq_printf(p, "%10u ", cpu_pda(j)->__nmi_count); | |
1da177e4 LT |
94 | seq_putc(p, '\n'); |
95 | #ifdef CONFIG_X86_LOCAL_APIC | |
96 | seq_printf(p, "LOC: "); | |
394e3902 AM |
97 | for_each_online_cpu(j) |
98 | seq_printf(p, "%10u ", cpu_pda(j)->apic_timer_irqs); | |
1da177e4 LT |
99 | seq_putc(p, '\n'); |
100 | #endif | |
101 | seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); | |
102 | #ifdef CONFIG_X86_IO_APIC | |
103 | #ifdef APIC_MISMATCH_DEBUG | |
104 | seq_printf(p, "MIS: %10u\n", atomic_read(&irq_mis_count)); | |
105 | #endif | |
106 | #endif | |
107 | } | |
108 | return 0; | |
109 | } | |
110 | ||
111 | /* | |
112 | * do_IRQ handles all normal device IRQ's (the special | |
113 | * SMP cross-CPU interrupts have their own specific | |
114 | * handlers). | |
115 | */ | |
116 | asmlinkage unsigned int do_IRQ(struct pt_regs *regs) | |
117 | { | |
19eadf98 RR |
118 | /* high bit used in ret_from_ code */ |
119 | unsigned irq = ~regs->orig_rax; | |
1da177e4 | 120 | |
a052b68b AM |
121 | if (unlikely(irq >= NR_IRQS)) { |
122 | printk(KERN_EMERG "%s: cannot handle IRQ %d\n", | |
123 | __FUNCTION__, irq); | |
124 | BUG(); | |
125 | } | |
126 | ||
95833c83 | 127 | exit_idle(); |
1da177e4 | 128 | irq_enter(); |
4961f10e ES |
129 | #ifdef CONFIG_DEBUG_STACKOVERFLOW |
130 | stack_overflow_check(regs); | |
131 | #endif | |
1da177e4 LT |
132 | __do_IRQ(irq, regs); |
133 | irq_exit(); | |
134 | ||
135 | return 1; | |
136 | } | |
137 | ||
76e4f660 AR |
138 | #ifdef CONFIG_HOTPLUG_CPU |
139 | void fixup_irqs(cpumask_t map) | |
140 | { | |
141 | unsigned int irq; | |
142 | static int warned; | |
143 | ||
144 | for (irq = 0; irq < NR_IRQS; irq++) { | |
145 | cpumask_t mask; | |
146 | if (irq == 2) | |
147 | continue; | |
148 | ||
149 | cpus_and(mask, irq_affinity[irq], map); | |
150 | if (any_online_cpu(mask) == NR_CPUS) { | |
151 | printk("Breaking affinity for irq %i\n", irq); | |
152 | mask = map; | |
153 | } | |
154 | if (irq_desc[irq].handler->set_affinity) | |
155 | irq_desc[irq].handler->set_affinity(irq, mask); | |
156 | else if (irq_desc[irq].action && !(warned++)) | |
157 | printk("Cannot set affinity for irq %i\n", irq); | |
158 | } | |
159 | ||
160 | /* That doesn't seem sufficient. Give it 1ms. */ | |
161 | local_irq_enable(); | |
162 | mdelay(1); | |
163 | local_irq_disable(); | |
164 | } | |
165 | #endif | |
ed6b676c AK |
166 | |
167 | extern void call_softirq(void); | |
168 | ||
169 | asmlinkage void do_softirq(void) | |
170 | { | |
171 | __u32 pending; | |
172 | unsigned long flags; | |
173 | ||
174 | if (in_interrupt()) | |
175 | return; | |
176 | ||
177 | local_irq_save(flags); | |
178 | pending = local_softirq_pending(); | |
179 | /* Switch to interrupt stack */ | |
180 | if (pending) | |
181 | call_softirq(); | |
182 | local_irq_restore(flags); | |
183 | } | |
184 | EXPORT_SYMBOL(do_softirq); |