2009-04-09 08:52:26 +00:00
|
|
|
#include <linux/linkage.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/signal.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/interrupt.h>
|
2009-04-09 08:52:26 +00:00
|
|
|
#include <linux/timex.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/random.h>
|
2009-04-10 12:58:05 +00:00
|
|
|
#include <linux/kprobes.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/kernel_stat.h>
|
|
|
|
#include <linux/sysdev.h>
|
|
|
|
#include <linux/bitops.h>
|
2009-04-09 08:52:26 +00:00
|
|
|
#include <linux/acpi.h>
|
2009-01-04 11:05:17 +00:00
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/delay.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
#include <asm/atomic.h>
|
|
|
|
#include <asm/system.h>
|
|
|
|
#include <asm/timer.h>
|
2009-04-09 08:52:26 +00:00
|
|
|
#include <asm/hw_irq.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <asm/pgtable.h>
|
|
|
|
#include <asm/desc.h>
|
|
|
|
#include <asm/apic.h>
|
2009-02-22 23:34:39 +00:00
|
|
|
#include <asm/setup.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <asm/i8259.h>
|
2009-01-04 11:05:17 +00:00
|
|
|
#include <asm/traps.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-04-09 08:52:26 +00:00
|
|
|
/*
|
|
|
|
* ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts:
|
|
|
|
* (these are usually mapped to vectors 0x30-0x3f)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The IO-APIC gives us many more interrupt sources. Most of these
|
|
|
|
* are unused but an SMP system is supposed to have enough memory ...
|
|
|
|
* sometimes (mostly wrt. hw bugs) we get corrupted vectors all
|
|
|
|
* across the spectrum, so we really want to be prepared to get all
|
|
|
|
* of these. Plus, more powerful systems might have more than 64
|
|
|
|
* IO-APIC registers.
|
|
|
|
*
|
|
|
|
* (these are usually mapped into the 0x30-0xff vector range)
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-04-09 08:52:25 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Note that on a 486, we don't want to do a SIGFPE on an irq13
|
|
|
|
* as the irq is unreliable, and exception 16 works correctly
|
|
|
|
* (ie as explained in the intel literature). On a 386, you
|
|
|
|
* can't use exception 16 due to bad IBM design, so we have to
|
|
|
|
* rely on the less exact irq13.
|
|
|
|
*
|
|
|
|
* Careful.. Not only is IRQ13 unreliable, but it is also
|
|
|
|
* leads to races. IBM designers who came up with it should
|
|
|
|
* be shot.
|
|
|
|
*/
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 13:55:46 +00:00
|
|
|
static irqreturn_t math_error_irq(int cpl, void *dev_id)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-01-04 11:05:17 +00:00
|
|
|
outb(0, 0xF0);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (ignore_fpu_irq || !boot_cpu_data.hard_math)
|
|
|
|
return IRQ_NONE;
|
2008-01-30 12:30:56 +00:00
|
|
|
math_error((void __user *)get_irq_regs()->ip);
|
2005-04-16 22:20:36 +00:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* New motherboards sometimes make IRQ 13 be a PCI interrupt,
|
|
|
|
* so allow interrupt sharing.
|
|
|
|
*/
|
2007-10-17 16:04:36 +00:00
|
|
|
static struct irqaction fpu_irq = {
|
|
|
|
.handler = math_error_irq,
|
|
|
|
.name = "fpu",
|
|
|
|
};
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif
|
|
|
|
|
2008-08-11 14:34:08 +00:00
|
|
|
/*
|
|
|
|
* IRQ2 is cascade interrupt to second interrupt controller
|
|
|
|
*/
|
|
|
|
static struct irqaction irq2 = {
|
|
|
|
.handler = no_action,
|
|
|
|
.name = "cascade",
|
|
|
|
};
|
|
|
|
|
2008-08-20 03:50:28 +00:00
|
|
|
DEFINE_PER_CPU(vector_irq_t, vector_irq) = {
|
|
|
|
[0 ... IRQ0_VECTOR - 1] = -1,
|
|
|
|
[IRQ0_VECTOR] = 0,
|
|
|
|
[IRQ1_VECTOR] = 1,
|
|
|
|
[IRQ2_VECTOR] = 2,
|
|
|
|
[IRQ3_VECTOR] = 3,
|
|
|
|
[IRQ4_VECTOR] = 4,
|
|
|
|
[IRQ5_VECTOR] = 5,
|
|
|
|
[IRQ6_VECTOR] = 6,
|
|
|
|
[IRQ7_VECTOR] = 7,
|
|
|
|
[IRQ8_VECTOR] = 8,
|
|
|
|
[IRQ9_VECTOR] = 9,
|
|
|
|
[IRQ10_VECTOR] = 10,
|
|
|
|
[IRQ11_VECTOR] = 11,
|
|
|
|
[IRQ12_VECTOR] = 12,
|
|
|
|
[IRQ13_VECTOR] = 13,
|
|
|
|
[IRQ14_VECTOR] = 14,
|
|
|
|
[IRQ15_VECTOR] = 15,
|
|
|
|
[IRQ15_VECTOR + 1 ... NR_VECTORS - 1] = -1
|
|
|
|
};
|
|
|
|
|
2008-12-19 23:23:44 +00:00
|
|
|
int vector_used_by_percpu_irq(unsigned int vector)
|
|
|
|
{
|
|
|
|
int cpu;
|
|
|
|
|
|
|
|
for_each_online_cpu(cpu) {
|
|
|
|
if (per_cpu(vector_irq, cpu)[vector] != -1)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-20 07:41:38 +00:00
|
|
|
void __init init_ISA_irqs(void)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2009-04-09 08:52:24 +00:00
|
|
|
#if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC)
|
2009-04-09 08:52:19 +00:00
|
|
|
init_bsp_APIC();
|
|
|
|
#endif
|
|
|
|
init_8259A(0);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
2009-04-09 08:52:19 +00:00
|
|
|
* 16 old-style INTA-cycle interrupts:
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2009-04-09 08:52:19 +00:00
|
|
|
for (i = 0; i < NR_IRQS_LEGACY; i++) {
|
|
|
|
struct irq_desc *desc = irq_to_desc(i);
|
|
|
|
|
|
|
|
desc->status = IRQ_DISABLED;
|
|
|
|
desc->action = NULL;
|
|
|
|
desc->depth = 1;
|
|
|
|
|
|
|
|
set_irq_chip_and_handler_name(i, &i8259A_chip,
|
|
|
|
handle_level_irq, "XT");
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-04-09 08:52:19 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-09-16 06:42:26 +00:00
|
|
|
void __init init_IRQ(void)
|
2009-08-20 07:59:09 +00:00
|
|
|
{
|
|
|
|
x86_init.irqs.intr_init();
|
|
|
|
}
|
2008-08-11 14:34:08 +00:00
|
|
|
|
2009-04-09 08:52:20 +00:00
|
|
|
static void __init smp_intr_init(void)
|
|
|
|
{
|
2009-04-09 08:52:23 +00:00
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
#if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC)
|
2008-08-11 14:34:08 +00:00
|
|
|
/*
|
|
|
|
* The reschedule interrupt is a CPU-to-CPU reschedule-helper
|
|
|
|
* IPI, driven by wakeup.
|
|
|
|
*/
|
|
|
|
alloc_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
|
|
|
|
|
2009-01-21 08:26:06 +00:00
|
|
|
/* IPIs for invalidation */
|
|
|
|
alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+0, invalidate_interrupt0);
|
|
|
|
alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+1, invalidate_interrupt1);
|
|
|
|
alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+2, invalidate_interrupt2);
|
|
|
|
alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+3, invalidate_interrupt3);
|
|
|
|
alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+4, invalidate_interrupt4);
|
|
|
|
alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+5, invalidate_interrupt5);
|
|
|
|
alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+6, invalidate_interrupt6);
|
|
|
|
alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+7, invalidate_interrupt7);
|
2008-08-11 14:34:08 +00:00
|
|
|
|
|
|
|
/* IPI for generic function call */
|
|
|
|
alloc_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
|
|
|
|
|
2009-04-09 08:52:23 +00:00
|
|
|
/* IPI for generic single function call */
|
2008-12-19 23:23:44 +00:00
|
|
|
alloc_intr_gate(CALL_FUNCTION_SINGLE_VECTOR,
|
2009-04-09 08:52:23 +00:00
|
|
|
call_function_single_interrupt);
|
2008-08-20 03:50:28 +00:00
|
|
|
|
|
|
|
/* Low priority IPI to cleanup after moving an irq */
|
|
|
|
set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt);
|
2008-12-19 23:23:44 +00:00
|
|
|
set_bit(IRQ_MOVE_CLEANUP_VECTOR, used_vectors);
|
x86: fix panic with interrupts off (needed for MCE)
For some time each panic() called with interrupts disabled
triggered the !irqs_disabled() WARN_ON in smp_call_function(),
producing ugly backtraces and confusing users.
This is a common situation with machine checks for example which
tend to call panic with interrupts disabled, but will also hit
in other situations e.g. panic during early boot. In fact it
means that panic cannot be called in many circumstances, which
would be bad.
This all started with the new fancy queued smp_call_function,
which is then used by the shutdown path to shut down the other
CPUs.
On closer examination it turned out that the fancy RCU
smp_call_function() does lots of things not suitable in a panic
situation anyways, like allocating memory and relying on complex
system state.
I originally tried to patch this over by checking for panic
there, but it was quite complicated and the original patch
was also not very popular. This also didn't fix some of the
underlying complexity problems.
The new code in post 2.6.29 tries to patch around this by
checking for oops_in_progress, but that is not enough to make
this fully safe and I don't think that's a real solution
because panic has to be reliable.
So instead use an own vector to reboot. This makes the reboot
code extremly straight forward, which is definitely a big plus
in a panic situation where it is important to avoid relying on
too much kernel state. The new simple code is also safe to be
called from interupts off region because it is very very simple.
There can be situations where it is important that panic
is reliable. For example on a fatal machine check the panic
is needed to get the system up again and running as quickly
as possible. So it's important that panic is reliable and
all function it calls simple.
This is why I came up with this simple vector scheme.
It's very hard to beat in simplicity. Vectors are not
particularly precious anymore since all big systems are
using per CPU vectors.
Another possibility would have been to use an NMI similar
to kdump, but there is still the problem that NMIs don't
work reliably on some systems due to BIOS issues. NMIs
would have been able to stop CPUs running with interrupts
off too. In the sake of universal reliability I opted for
using a non NMI vector for now.
I put the reboot vector into the highest priority bucket of
the APIC vectors and moved the 64bit UV_BAU message down
instead into the next lower priority.
[ Impact: bug fix, fixes an old regression ]
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
2009-05-27 19:56:52 +00:00
|
|
|
|
|
|
|
/* IPI used for rebooting/stopping */
|
|
|
|
alloc_intr_gate(REBOOT_VECTOR, reboot_interrupt);
|
2008-08-11 14:34:08 +00:00
|
|
|
#endif
|
2009-04-09 08:52:23 +00:00
|
|
|
#endif /* CONFIG_SMP */
|
2009-04-09 08:52:20 +00:00
|
|
|
}
|
|
|
|
|
2009-04-09 08:52:21 +00:00
|
|
|
static void __init apic_intr_init(void)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-04-09 08:52:20 +00:00
|
|
|
smp_intr_init();
|
2008-08-11 14:34:08 +00:00
|
|
|
|
2009-06-01 22:13:02 +00:00
|
|
|
#ifdef CONFIG_X86_THERMAL_VECTOR
|
2009-04-09 08:52:27 +00:00
|
|
|
alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
|
2009-06-01 22:13:02 +00:00
|
|
|
#endif
|
2009-07-22 02:56:20 +00:00
|
|
|
#ifdef CONFIG_X86_MCE_THRESHOLD
|
2009-04-09 08:52:27 +00:00
|
|
|
alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt);
|
|
|
|
#endif
|
2009-05-27 19:56:54 +00:00
|
|
|
#if defined(CONFIG_X86_NEW_MCE) && defined(CONFIG_X86_LOCAL_APIC)
|
|
|
|
alloc_intr_gate(MCE_SELF_VECTOR, mce_self_interrupt);
|
|
|
|
#endif
|
2009-04-09 08:52:27 +00:00
|
|
|
|
|
|
|
#if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC)
|
2008-08-11 14:34:08 +00:00
|
|
|
/* self generated IPI for local APIC timer */
|
|
|
|
alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
|
|
|
|
|
2009-03-04 18:56:05 +00:00
|
|
|
/* generic IPI for platform specific use */
|
|
|
|
alloc_intr_gate(GENERIC_INTERRUPT_VECTOR, generic_interrupt);
|
|
|
|
|
2008-08-11 14:34:08 +00:00
|
|
|
/* IPI vectors for APIC spurious and error interrupts */
|
|
|
|
alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
|
|
|
|
alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
|
|
|
|
|
2009-04-10 12:58:05 +00:00
|
|
|
/* Performance monitoring interrupts: */
|
|
|
|
# ifdef CONFIG_PERF_COUNTERS
|
|
|
|
alloc_intr_gate(LOCAL_PENDING_VECTOR, perf_pending_interrupt);
|
|
|
|
# endif
|
|
|
|
|
2008-08-11 14:34:08 +00:00
|
|
|
#endif
|
2009-04-09 08:52:21 +00:00
|
|
|
}
|
2008-08-11 14:34:08 +00:00
|
|
|
|
2009-04-09 08:52:21 +00:00
|
|
|
void __init native_init_IRQ(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Execute any quirks before the call gates are initialised: */
|
2009-08-20 07:41:38 +00:00
|
|
|
x86_init.irqs.pre_vector_init();
|
2009-04-09 08:52:21 +00:00
|
|
|
|
2009-04-15 18:57:01 +00:00
|
|
|
apic_intr_init();
|
|
|
|
|
2009-04-09 08:52:21 +00:00
|
|
|
/*
|
|
|
|
* Cover the whole vector space, no vector can escape
|
|
|
|
* us. (some of these will be overridden and become
|
|
|
|
* 'special' SMP interrupts)
|
|
|
|
*/
|
2009-04-09 08:52:22 +00:00
|
|
|
for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) {
|
2009-04-15 18:57:01 +00:00
|
|
|
/* IA32_SYSCALL_VECTOR could be used in trap_init already. */
|
|
|
|
if (!test_bit(i, used_vectors))
|
2009-04-09 08:52:25 +00:00
|
|
|
set_intr_gate(i, interrupt[i-FIRST_EXTERNAL_VECTOR]);
|
2009-04-09 08:52:21 +00:00
|
|
|
}
|
2009-04-28 21:32:56 +00:00
|
|
|
|
2008-08-11 14:34:08 +00:00
|
|
|
if (!acpi_ioapic)
|
|
|
|
setup_irq(2, &irq2);
|
|
|
|
|
2009-04-09 08:52:25 +00:00
|
|
|
#ifdef CONFIG_X86_32
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* External FPU? Set up irq13 if so, for
|
|
|
|
* original braindamaged IBM FERR coupling.
|
|
|
|
*/
|
|
|
|
if (boot_cpu_data.hard_math && !cpu_has_fpu)
|
|
|
|
setup_irq(FPU_IRQ, &fpu_irq);
|
|
|
|
|
|
|
|
irq_ctx_init(smp_processor_id());
|
2009-04-09 08:52:25 +00:00
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|