Commit 1b8ba39a authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'x86/irq' into x86/devel

Conflicts:

	arch/x86/kernel/i8259.c
	arch/x86/kernel/irqinit_64.c
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parents 58cf3522 cbd67124
...@@ -514,8 +514,6 @@ int acpi_register_gsi(u32 gsi, int triggering, int polarity) ...@@ -514,8 +514,6 @@ int acpi_register_gsi(u32 gsi, int triggering, int polarity)
* Make sure all (legacy) PCI IRQs are set as level-triggered. * Make sure all (legacy) PCI IRQs are set as level-triggered.
*/ */
if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) { if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) {
extern void eisa_set_level_irq(unsigned int irq);
if (triggering == ACPI_LEVEL_SENSITIVE) if (triggering == ACPI_LEVEL_SENSITIVE)
eisa_set_level_irq(gsi); eisa_set_level_irq(gsi);
} }
......
...@@ -70,6 +70,10 @@ static int local_apic_timer_disabled; ...@@ -70,6 +70,10 @@ static int local_apic_timer_disabled;
int local_apic_timer_c2_ok; int local_apic_timer_c2_ok;
EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok); EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok);
int first_system_vector = 0xfe;
char system_vectors[NR_VECTORS] = { [0 ... NR_VECTORS-1] = SYS_VECTOR_FREE};
/* /*
* Debug level, exported for io_apic.c * Debug level, exported for io_apic.c
*/ */
...@@ -1351,13 +1355,13 @@ void __init smp_intr_init(void) ...@@ -1351,13 +1355,13 @@ void __init smp_intr_init(void)
* The reschedule interrupt is a CPU-to-CPU reschedule-helper * The reschedule interrupt is a CPU-to-CPU reschedule-helper
* IPI, driven by wakeup. * IPI, driven by wakeup.
*/ */
set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); alloc_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
/* IPI for invalidation */ /* IPI for invalidation */
set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); alloc_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
/* IPI for generic function call */ /* IPI for generic function call */
set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); alloc_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
} }
#endif #endif
...@@ -1370,15 +1374,15 @@ void __init apic_intr_init(void) ...@@ -1370,15 +1374,15 @@ void __init apic_intr_init(void)
smp_intr_init(); smp_intr_init();
#endif #endif
/* self generated IPI for local APIC timer */ /* self generated IPI for local APIC timer */
set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
/* IPI vectors for APIC spurious and error interrupts */ /* IPI vectors for APIC spurious and error interrupts */
set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
set_intr_gate(ERROR_APIC_VECTOR, error_interrupt); alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
/* thermal monitor LVT interrupt */ /* thermal monitor LVT interrupt */
#ifdef CONFIG_X86_MCE_P4THERMAL #ifdef CONFIG_X86_MCE_P4THERMAL
set_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt); alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
#endif #endif
} }
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
#include <asm/percpu.h> #include <asm/percpu.h>
#include <asm/dwarf2.h> #include <asm/dwarf2.h>
#include <asm/processor-flags.h> #include <asm/processor-flags.h>
#include "irq_vectors.h" #include <asm/irq_vectors.h>
/* /*
* We use macros for low-level operations which need to be overridden * We use macros for low-level operations which need to be overridden
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* SGI UV APIC functions (note: not an Intel compatible APIC) * SGI UV APIC functions (note: not an Intel compatible APIC)
* *
* Copyright (C) 2007 Silicon Graphics, Inc. All rights reserved. * Copyright (C) 2007-2008 Silicon Graphics, Inc. All rights reserved.
*/ */
#include <linux/threads.h> #include <linux/threads.h>
...@@ -55,37 +55,37 @@ static cpumask_t uv_vector_allocation_domain(int cpu) ...@@ -55,37 +55,37 @@ static cpumask_t uv_vector_allocation_domain(int cpu)
int uv_wakeup_secondary(int phys_apicid, unsigned int start_rip) int uv_wakeup_secondary(int phys_apicid, unsigned int start_rip)
{ {
unsigned long val; unsigned long val;
int nasid; int pnode;
nasid = uv_apicid_to_nasid(phys_apicid); pnode = uv_apicid_to_pnode(phys_apicid);
val = (1UL << UVH_IPI_INT_SEND_SHFT) | val = (1UL << UVH_IPI_INT_SEND_SHFT) |
(phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) | (phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) |
(((long)start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) | (((long)start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) |
APIC_DM_INIT; APIC_DM_INIT;
uv_write_global_mmr64(nasid, UVH_IPI_INT, val); uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
mdelay(10); mdelay(10);
val = (1UL << UVH_IPI_INT_SEND_SHFT) | val = (1UL << UVH_IPI_INT_SEND_SHFT) |
(phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) | (phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) |
(((long)start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) | (((long)start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) |
APIC_DM_STARTUP; APIC_DM_STARTUP;
uv_write_global_mmr64(nasid, UVH_IPI_INT, val); uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
return 0; return 0;
} }
static void uv_send_IPI_one(int cpu, int vector) static void uv_send_IPI_one(int cpu, int vector)
{ {
unsigned long val, apicid, lapicid; unsigned long val, apicid, lapicid;
int nasid; int pnode;
apicid = per_cpu(x86_cpu_to_apicid, cpu); /* ZZZ - cache node-local ? */ apicid = per_cpu(x86_cpu_to_apicid, cpu); /* ZZZ - cache node-local ? */
lapicid = apicid & 0x3f; /* ZZZ macro needed */ lapicid = apicid & 0x3f; /* ZZZ macro needed */
nasid = uv_apicid_to_nasid(apicid); pnode = uv_apicid_to_pnode(apicid);
val = val =
(1UL << UVH_IPI_INT_SEND_SHFT) | (lapicid << (1UL << UVH_IPI_INT_SEND_SHFT) | (lapicid <<
UVH_IPI_INT_APIC_ID_SHFT) | UVH_IPI_INT_APIC_ID_SHFT) |
(vector << UVH_IPI_INT_VECTOR_SHFT); (vector << UVH_IPI_INT_VECTOR_SHFT);
uv_write_global_mmr64(nasid, UVH_IPI_INT, val); uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
} }
static void uv_send_IPI_mask(cpumask_t mask, int vector) static void uv_send_IPI_mask(cpumask_t mask, int vector)
...@@ -159,39 +159,81 @@ struct genapic apic_x2apic_uv_x = { ...@@ -159,39 +159,81 @@ struct genapic apic_x2apic_uv_x = {
.phys_pkg_id = phys_pkg_id, /* Fixme ZZZ */ .phys_pkg_id = phys_pkg_id, /* Fixme ZZZ */
}; };
static __cpuinit void set_x2apic_extra_bits(int nasid) static __cpuinit void set_x2apic_extra_bits(int pnode)
{ {
__get_cpu_var(x2apic_extra_bits) = ((nasid >> 1) << 6); __get_cpu_var(x2apic_extra_bits) = (pnode << 6);
} }
/* /*
* Called on boot cpu. * Called on boot cpu.
*/ */
static __init int boot_pnode_to_blade(int pnode)
{
int blade;
for (blade = 0; blade < uv_num_possible_blades(); blade++)
if (pnode == uv_blade_info[blade].pnode)
return blade;
BUG();
}
struct redir_addr {
unsigned long redirect;
unsigned long alias;
};
#define DEST_SHIFT UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR_DEST_BASE_SHFT
static __initdata struct redir_addr redir_addrs[] = {
{UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR, UVH_SI_ALIAS0_OVERLAY_CONFIG},
{UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR, UVH_SI_ALIAS1_OVERLAY_CONFIG},
{UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR, UVH_SI_ALIAS2_OVERLAY_CONFIG},
};
static __init void get_lowmem_redirect(unsigned long *base, unsigned long *size)
{
union uvh_si_alias0_overlay_config_u alias;
union uvh_rh_gam_alias210_redirect_config_2_mmr_u redirect;
int i;
for (i = 0; i < ARRAY_SIZE(redir_addrs); i++) {
alias.v = uv_read_local_mmr(redir_addrs[i].alias);
if (alias.s.base == 0) {
*size = (1UL << alias.s.m_alias);
redirect.v = uv_read_local_mmr(redir_addrs[i].redirect);
*base = (unsigned long)redirect.s.dest_base << DEST_SHIFT;
return;
}
}
BUG();
}
static __init void uv_system_init(void) static __init void uv_system_init(void)
{ {
union uvh_si_addr_map_config_u m_n_config; union uvh_si_addr_map_config_u m_n_config;
int bytes, nid, cpu, lcpu, nasid, last_nasid, blade; union uvh_node_id_u node_id;
unsigned long mmr_base; unsigned long gnode_upper, lowmem_redir_base, lowmem_redir_size;
int bytes, nid, cpu, lcpu, pnode, blade, i, j, m_val, n_val;
unsigned long mmr_base, present;
m_n_config.v = uv_read_local_mmr(UVH_SI_ADDR_MAP_CONFIG); m_n_config.v = uv_read_local_mmr(UVH_SI_ADDR_MAP_CONFIG);
m_val = m_n_config.s.m_skt;
n_val = m_n_config.s.n_skt;
mmr_base = mmr_base =
uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR) & uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR) &
~UV_MMR_ENABLE; ~UV_MMR_ENABLE;
printk(KERN_DEBUG "UV: global MMR base 0x%lx\n", mmr_base); printk(KERN_DEBUG "UV: global MMR base 0x%lx\n", mmr_base);
last_nasid = -1; for(i = 0; i < UVH_NODE_PRESENT_TABLE_DEPTH; i++)
for_each_possible_cpu(cpu) { uv_possible_blades +=
nid = cpu_to_node(cpu); hweight64(uv_read_local_mmr( UVH_NODE_PRESENT_TABLE + i * 8));
nasid = uv_apicid_to_nasid(per_cpu(x86_cpu_to_apicid, cpu));
if (nasid != last_nasid)
uv_possible_blades++;
last_nasid = nasid;
}
printk(KERN_DEBUG "UV: Found %d blades\n", uv_num_possible_blades()); printk(KERN_DEBUG "UV: Found %d blades\n", uv_num_possible_blades());
bytes = sizeof(struct uv_blade_info) * uv_num_possible_blades(); bytes = sizeof(struct uv_blade_info) * uv_num_possible_blades();
uv_blade_info = alloc_bootmem_pages(bytes); uv_blade_info = alloc_bootmem_pages(bytes);
get_lowmem_redirect(&lowmem_redir_base, &lowmem_redir_size);
bytes = sizeof(uv_node_to_blade[0]) * num_possible_nodes(); bytes = sizeof(uv_node_to_blade[0]) * num_possible_nodes();
uv_node_to_blade = alloc_bootmem_pages(bytes); uv_node_to_blade = alloc_bootmem_pages(bytes);
memset(uv_node_to_blade, 255, bytes); memset(uv_node_to_blade, 255, bytes);
...@@ -200,43 +242,56 @@ static __init void uv_system_init(void) ...@@ -200,43 +242,56 @@ static __init void uv_system_init(void)
uv_cpu_to_blade = alloc_bootmem_pages(bytes); uv_cpu_to_blade = alloc_bootmem_pages(bytes);
memset(uv_cpu_to_blade, 255, bytes); memset(uv_cpu_to_blade, 255, bytes);
last_nasid = -1; blade = 0;
blade = -1; for (i = 0; i < UVH_NODE_PRESENT_TABLE_DEPTH; i++) {
lcpu = -1; present = uv_read_local_mmr(UVH_NODE_PRESENT_TABLE + i * 8);
for_each_possible_cpu(cpu) { for (j = 0; j < 64; j++) {
nid = cpu_to_node(cpu); if (!test_bit(j, &present))
nasid = uv_apicid_to_nasid(per_cpu(x86_cpu_to_apicid, cpu)); continue;
if (nasid != last_nasid) { uv_blade_info[blade].pnode = (i * 64 + j);
blade++; uv_blade_info[blade].nr_possible_cpus = 0;
lcpu = -1;
uv_blade_info[blade].nr_posible_cpus = 0;
uv_blade_info[blade].nr_online_cpus = 0; uv_blade_info[blade].nr_online_cpus = 0;
blade++;
} }
last_nasid = nasid; }
lcpu++;
uv_cpu_hub_info(cpu)->m_val = m_n_config.s.m_skt; node_id.v = uv_read_local_mmr(UVH_NODE_ID);
uv_cpu_hub_info(cpu)->n_val = m_n_config.s.n_skt; gnode_upper = (((unsigned long)node_id.s.node_id) &
~((1 << n_val) - 1)) << m_val;
for_each_present_cpu(cpu) {
nid = cpu_to_node(cpu);
pnode = uv_apicid_to_pnode(per_cpu(x86_cpu_to_apicid, cpu));
blade = boot_pnode_to_blade(pnode);
lcpu = uv_blade_info[blade].nr_possible_cpus;
uv_blade_info[blade].nr_possible_cpus++;
uv_cpu_hub_info(cpu)->lowmem_remap_base = lowmem_redir_base;
uv_cpu_hub_info(cpu)->lowmem_remap_top =
lowmem_redir_base + lowmem_redir_size;
uv_cpu_hub_info(cpu)->m_val = m_val;
uv_cpu_hub_info(cpu)->n_val = m_val;
uv_cpu_hub_info(cpu)->numa_blade_id = blade; uv_cpu_hub_info(cpu)->numa_blade_id = blade;
uv_cpu_hub_info(cpu)->blade_processor_id = lcpu; uv_cpu_hub_info(cpu)->blade_processor_id = lcpu;
uv_cpu_hub_info(cpu)->local_nasid = nasid; uv_cpu_hub_info(cpu)->pnode = pnode;
uv_cpu_hub_info(cpu)->gnode_upper = uv_cpu_hub_info(cpu)->pnode_mask = (1 << n_val) - 1;
nasid & ~((1 << uv_hub_info->n_val) - 1); uv_cpu_hub_info(cpu)->gpa_mask = (1 << (m_val + n_val)) - 1;
uv_cpu_hub_info(cpu)->gnode_upper = gnode_upper;
uv_cpu_hub_info(cpu)->global_mmr_base = mmr_base; uv_cpu_hub_info(cpu)->global_mmr_base = mmr_base;
uv_cpu_hub_info(cpu)->coherency_domain_number = 0;/* ZZZ */ uv_cpu_hub_info(cpu)->coherency_domain_number = 0;/* ZZZ */
uv_blade_info[blade].nasid = nasid;
uv_blade_info[blade].nr_posible_cpus++;
uv_node_to_blade[nid] = blade; uv_node_to_blade[nid] = blade;
uv_cpu_to_blade[cpu] = blade; uv_cpu_to_blade[cpu] = blade;
printk(KERN_DEBUG "UV cpu %d, apicid 0x%x, nasid %d, nid %d\n", printk(KERN_DEBUG "UV cpu %d, apicid 0x%x, pnode %d, nid %d, "
cpu, per_cpu(x86_cpu_to_apicid, cpu), nasid, nid); "lcpu %d, blade %d\n",
printk(KERN_DEBUG "UV lcpu %d, blade %d\n", lcpu, blade); cpu, per_cpu(x86_cpu_to_apicid, cpu), pnode, nid,
lcpu, blade);
} }
} }
/* /*
* Called on each cpu to initialize the per_cpu UV data area. * Called on each cpu to initialize the per_cpu UV data area.
* ZZZ hotplug not supported yet
*/ */
void __cpuinit uv_cpu_init(void) void __cpuinit uv_cpu_init(void)
{ {
...@@ -246,5 +301,5 @@ void __cpuinit uv_cpu_init(void) ...@@ -246,5 +301,5 @@ void __cpuinit uv_cpu_init(void)
uv_blade_info[uv_numa_blade_id()].nr_online_cpus++; uv_blade_info[uv_numa_blade_id()].nr_online_cpus++;
if (get_uv_system_type() == UV_NON_UNIQUE_APIC) if (get_uv_system_type() == UV_NON_UNIQUE_APIC)
set_x2apic_extra_bits(uv_hub_info->local_nasid); set_x2apic_extra_bits(uv_hub_info->pnode);
} }
...@@ -297,34 +297,28 @@ void init_8259A(int auto_eoi) ...@@ -297,34 +297,28 @@ void init_8259A(int auto_eoi)
* outb_pic - this has to work on a wide range of PC hardware. * outb_pic - this has to work on a wide range of PC hardware.
*/ */
outb_pic(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */ outb_pic(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
#ifndef CONFIG_X86_64
outb_pic(0x20 + 0, PIC_MASTER_IMR); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */ /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 on x86-64,
outb_pic(1U << PIC_CASCADE_IR, PIC_MASTER_IMR); /* 8259A-1 (the master) has a slave on IR2 */ to 0x20-0x27 on i386 */
#else /* CONFIG_X86_64 */
/* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 */
outb_pic(IRQ0_VECTOR, PIC_MASTER_IMR); outb_pic(IRQ0_VECTOR, PIC_MASTER_IMR);
/* 8259A-1 (the master) has a slave on IR2 */ /* 8259A-1 (the master) has a slave on IR2 */
outb_pic(0x04, PIC_MASTER_IMR); outb_pic(1U << PIC_CASCADE_IR, PIC_MASTER_IMR);
#endif /* CONFIG_X86_64 */
if (auto_eoi) /* master does Auto EOI */ if (auto_eoi) /* master does Auto EOI */
outb_pic(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR); outb_pic(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
else /* master expects normal EOI */ else /* master expects normal EOI */
outb_pic(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR); outb_pic(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
outb_pic(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */ outb_pic(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */
#ifndef CONFIG_X86_64
outb_pic(0x20 + 8, PIC_SLAVE_IMR); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */ /* ICW2: 8259A-2 IR0-7 mapped to IRQ8_VECTOR */
outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR); /* 8259A-2 is a slave on master's IR2 */
outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); /* (slave's support for AEOI in flat mode is to be investigated) */
#else /* CONFIG_X86_64 */
/* ICW2: 8259A-2 IR0-7 mapped to 0x38-0x3f */
outb_pic(IRQ8_VECTOR, PIC_SLAVE_IMR); outb_pic(IRQ8_VECTOR, PIC_SLAVE_IMR);
/* 8259A-2 is a slave on master's IR2 */ /* 8259A-2 is a slave on master's IR2 */
outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR); outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR);
/* (slave's support for AEOI in flat mode is to be investigated) */ /* (slave's support for AEOI in flat mode is to be investigated) */
outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR);
#endif /* CONFIG_X86_64 */
if (auto_eoi) if (auto_eoi)
/* /*
* In AEOI mode we just have to mask the interrupt * In AEOI mode we just have to mask the interrupt
......
...@@ -1174,7 +1174,7 @@ static int __assign_irq_vector(int irq) ...@@ -1174,7 +1174,7 @@ static int __assign_irq_vector(int irq)
offset = current_offset; offset = current_offset;
next: next:
vector += 8; vector += 8;
if (vector >= FIRST_SYSTEM_VECTOR) { if (vector >= first_system_vector) {
offset = (offset + 1) % 8; offset = (offset + 1) % 8;
vector = FIRST_DEVICE_VECTOR + offset; vector = FIRST_DEVICE_VECTOR + offset;
} }
...@@ -2280,7 +2280,7 @@ void __init setup_IO_APIC(void) ...@@ -2280,7 +2280,7 @@ void __init setup_IO_APIC(void)
int i; int i;
/* Reserve all the system vectors. */ /* Reserve all the system vectors. */
for (i = FIRST_SYSTEM_VECTOR; i < NR_VECTORS; i++) for (i = first_system_vector; i < NR_VECTORS; i++)
set_bit(i, used_vectors); set_bit(i, used_vectors);
enable_IO_APIC(); enable_IO_APIC();
......
...@@ -82,6 +82,10 @@ static struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = { ...@@ -82,6 +82,10 @@ static struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = {
static int assign_irq_vector(int irq, cpumask_t mask); static int assign_irq_vector(int irq, cpumask_t mask);
int first_system_vector = 0xfe;
char system_vectors[NR_VECTORS] = { [0 ... NR_VECTORS-1] = SYS_VECTOR_FREE};
#define __apicdebuginit __init #define __apicdebuginit __init
int sis_apic_bug; /* not actually supported, dummy for compile */ int sis_apic_bug; /* not actually supported, dummy for compile */
...@@ -737,7 +741,7 @@ static int __assign_irq_vector(int irq, cpumask_t mask) ...@@ -737,7 +741,7 @@ static int __assign_irq_vector(int irq, cpumask_t mask)
offset = current_offset; offset = current_offset;
next: next:
vector += 8; vector += 8;
if (vector >= FIRST_SYSTEM_VECTOR) { if (vector >= first_system_vector) {
/* If we run out of vectors on large boxen, must share them. */ /* If we run out of vectors on large boxen, must share them. */
offset = (offset + 1) % 8; offset = (offset + 1) % 8;
vector = FIRST_DEVICE_VECTOR + offset; vector = FIRST_DEVICE_VECTOR + offset;
......
...@@ -48,6 +48,29 @@ void ack_bad_irq(unsigned int irq) ...@@ -48,6 +48,29 @@ void ack_bad_irq(unsigned int irq)
#endif #endif
} }
#ifdef CONFIG_DEBUG_STACKOVERFLOW
/* Debugging check for stack overflow: is there less than 1KB free? */
static int check_stack_overflow(void)
{
long sp;
__asm__ __volatile__("andl %%esp,%0" :
"=r" (sp) : "0" (THREAD_SIZE - 1));
return sp < (sizeof(struct thread_info) + STACK_WARN);
}
static void print_stack_overflow(void)
{
printk(KERN_WARNING "low stack detected by irq handler\n");
dump_stack();
}
#else
static inline int check_stack_overflow(void) { return 0; }
static inline void print_stack_overflow(void) { }
#endif
#ifdef CONFIG_4KSTACKS #ifdef CONFIG_4KSTACKS
/* /*
* per-CPU IRQ handling contexts (thread information and stack) * per-CPU IRQ handling contexts (thread information and stack)
...@@ -59,48 +82,29 @@ union irq_ctx { ...@@ -59,48 +82,29 @@ union irq_ctx {
static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly; static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly;
static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly; static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly;
#endif
/* static char softirq_stack[NR_CPUS * THREAD_SIZE]
* do_IRQ handles all normal device IRQ's (the special __attribute__((__section__(".bss.page_aligned")));
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
unsigned int do_IRQ(struct pt_regs *regs)
{
struct pt_regs *old_regs;
/* high bit used in ret_from_ code */
int irq = ~regs->orig_ax;
struct irq_desc *desc = irq_desc + irq;
#ifdef CONFIG_4KSTACKS
union irq_ctx *curctx, *irqctx;
u32 *isp;
#endif
if (unlikely((unsigned)irq >= NR_IRQS)) { static char hardirq_stack[NR_CPUS * THREAD_SIZE]
printk(KERN_EMERG "%s: cannot handle IRQ %d\n", __attribute__((__section__(".bss.page_aligned")));
__func__, irq);
BUG();
}
old_regs = set_irq_regs(regs); static void call_on_stack(void *func, void *stack)
irq_enter(); {
#ifdef CONFIG_DEBUG_STACKOVERFLOW asm volatile("xchgl %%ebx,%%esp \n"
/* Debugging check for stack overflow: is there less than 1KB free? */ "call *%%edi \n"
{ "movl %%ebx,%%esp \n"
long sp; : "=b" (stack)
: "0" (stack),
__asm__ __volatile__("andl %%esp,%0" : "D"(func)
"=r" (sp) : "0" (THREAD_SIZE - 1)); : "memory", "cc", "edx", "ecx", "eax");
if (unlikely(sp < (sizeof(struct thread_info) + STACK_WARN))) { }
printk("do_IRQ: stack overflow: %ld\n",
sp - sizeof(struct thread_info));
dump_stack();
}
}
#endif
#ifdef CONFIG_4KSTACKS static inline int
execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq)
{
union irq_ctx *curctx, *irqctx;
u32 *isp, arg1, arg2;
curctx = (union irq_ctx *) current_thread_info(); curctx = (union irq_ctx *) current_thread_info();
irqctx = hardirq_ctx[smp_processor_id()]; irqctx = hardirq_ctx[smp_processor_id()];
...@@ -111,52 +115,39 @@ unsigned int do_IRQ(struct pt_regs *regs) ...@@ -111,52 +115,39 @@ unsigned int do_IRQ(struct pt_regs *regs)
* handler) we can't do that and just have to keep using the * handler) we can't do that and just have to keep using the
* current stack (which is the irq stack already after all) * current stack (which is the irq stack already after all)
*/ */
if (curctx != irqctx) { if (unlikely(curctx == irqctx))
int arg1, arg2, bx; return 0;
/* build the stack frame on the IRQ stack */ /* build the stack frame on the IRQ stack */
isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); isp = (u32 *) ((char*)irqctx + sizeof(*irqctx));
irqctx->tinfo.task = curctx->tinfo.task; irqctx->tinfo.task = curctx->tinfo.task;
irqctx->tinfo.previous_esp = current_stack_pointer; irqctx->tinfo.previous_esp = current_stack_pointer;
/* /*
* Copy the softirq bits in preempt_count so that the * Copy the softirq bits in preempt_count so that the
* softirq checks work in the hardirq context. * softirq checks work in the hardirq context.
*/ */
irqctx->tinfo.preempt_count = irqctx->tinfo.preempt_count =
(irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) | (irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) |
(curctx->tinfo.preempt_count & SOFTIRQ_MASK); (curctx->tinfo.preempt_count & SOFTIRQ_MASK);
asm volatile( if (unlikely(overflow))
" xchgl %%ebx,%%esp \n" call_on_stack(print_stack_overflow, isp);
" call *%%edi \n"
" movl %%ebx,%%esp \n" asm volatile("xchgl %%ebx,%%esp \n"
: "=a" (arg1), "=d" (arg2), "=b" (bx) "call *%%edi \n"
: "0" (irq), "1" (desc), "2" (isp), "movl %%ebx,%%esp \n"
"D" (desc->handle_irq) : "=a" (arg1), "=d" (arg2), "=b" (isp)
: "memory", "cc", "ecx" : "0" (irq), "1" (desc), "2" (isp),
); "D" (desc->handle_irq)
} else : "memory", "cc", "ecx");
#endif
desc->handle_irq(irq, desc);
irq_exit();
set_irq_regs(old_regs);
return 1; return 1;
} }
#ifdef CONFIG_4KSTACKS
static char softirq_stack[NR_CPUS * THREAD_SIZE]
__attribute__((__section__(".bss.page_aligned")));
static char hardirq_stack[NR_CPUS * THREAD_SIZE]
__attribute__((__section__(".bss.page_aligned")));
/* /*
* allocate per-cpu stacks for hardirq and for softirq processing * allocate per-cpu stacks for hardirq and for softirq processing
*/ */
void irq_ctx_init(int cpu) void __cpuinit irq_ctx_init(int cpu)
{ {
union irq_ctx *irqctx; union irq_ctx *irqctx;
...@@ -164,25 +155,25 @@ void irq_ctx_init(int cpu) ...@@ -164,25 +155,25 @@ void irq_ctx_init(int cpu)
return; return;
irqctx = (union irq_ctx*) &hardirq_stack[cpu*THREAD_SIZE]; irqctx = (union irq_ctx*) &hardirq_stack[cpu*THREAD_SIZE];
irqctx->tinfo.task = NULL; irqctx->tinfo.task = NULL;
irqctx->tinfo.exec_domain = NULL; irqctx->tinfo.exec_domain = NULL;
irqctx->tinfo.cpu = cpu; irqctx->tinfo.cpu = cpu;
irqctx->tinfo.preempt_count = HARDIRQ_OFFSET; irqctx->tinfo.preempt_count = HARDIRQ_OFFSET;
irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); irqctx->tinfo.addr_limit = MAKE_MM_SEG(0);
hardirq_ctx[cpu] = irqctx; hardirq_ctx[cpu] = irqctx;
irqctx = (union irq_ctx*) &softirq_stack[cpu*THREAD_SIZE]; irqctx = (union irq_ctx*) &softirq_stack[cpu*THREAD_SIZE];
irqctx->tinfo.task = NULL; irqctx->tinfo.task = NULL;
irqctx->tinfo.exec_domain = NULL; irqctx->tinfo.exec_domain = NULL;
irqctx->tinfo.cpu = cpu; irqctx->tinfo.cpu = cpu;
irqctx->tinfo.preempt_count = 0; irqctx->tinfo.preempt_count = 0;
irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); irqctx->tinfo.addr_limit = MAKE_MM_SEG(0);
softirq_ctx[cpu] = irqctx; softirq_ctx[cpu] = irqctx;
printk("CPU %u irqstacks, hard=%p soft=%p\n", printk(KERN_DEBUG "CPU %u irqstacks, hard=%p soft=%p\n",
cpu,hardirq_ctx[cpu],softirq_ctx[cpu]); cpu,hardirq_ctx[cpu],softirq_ctx[cpu]);
} }
void irq_ctx_exit(int cpu) void irq_ctx_exit(int cpu)
...@@ -211,24 +202,55 @@ asmlinkage void do_softirq(void) ...@@ -211,24 +202,55 @@ asmlinkage void do_softirq(void)
/* build the stack frame on the softirq stack */ /* build the stack frame on the softirq stack */
isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
asm volatile( call_on_stack(__do_softirq, isp);
" xchgl %%ebx,%%esp \n"
" call __do_softirq \n"
" movl %%ebx,%%esp \n"
: "=b"(isp)
: "0"(isp)
: "memory", "cc", "edx", "ecx", "eax"
);
/* /*
* Shouldnt happen, we returned above if in_interrupt(): * Shouldnt happen, we returned above if in_interrupt():
*/ */
WARN_ON_ONCE(softirq_count()); WARN_ON_ONCE(softirq_count());
} }
local_irq_restore(flags); local_irq_restore(flags);
} }
#else
static inline int
execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq) { return 0; }
#endif #endif
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
unsigned int do_IRQ(struct pt_regs *regs)
{
struct pt_regs *old_regs;
/* high bit used in ret_from_ code */
int overflow, irq = ~regs->orig_ax;
struct irq_desc *desc = irq_desc + irq;
if (unlikely((unsigned)irq >= NR_IRQS)) {
printk(KERN_EMERG "%s: cannot handle IRQ %d\n",
__func__, irq);
BUG();
}
old_regs = set_irq_regs(regs);
irq_enter();
overflow = check_stack_overflow();
if (!execute_on_irq_stack(overflow, desc, irq)) {
if (unlikely(overflow))
print_stack_overflow();
desc->handle_irq(irq, desc);
}
irq_exit();
set_irq_regs(old_regs);
return 1;
}
/* /*
* Interrupt statistics: * Interrupt statistics:
*/ */
......
...@@ -34,6 +34,20 @@ ...@@ -34,6 +34,20 @@
* interrupt-controller happy. * interrupt-controller happy.
*/ */
#define IRQ_NAME2(nr) nr##_interrupt(void)
#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
/*
* SMP has a few special interrupts for IPI messages
*/
#define BUILD_IRQ(nr) \
asmlinkage void IRQ_NAME(nr); \
asm("\n.p2align\n" \
"IRQ" #nr "_interrupt:\n\t" \
"push $~(" #nr ") ; " \
"jmp common_interrupt");
#define BI(x,y) \ #define BI(x,y) \
BUILD_IRQ(x##y) BUILD_IRQ(x##y)
...@@ -170,33 +184,33 @@ void __init native_init_IRQ(void) ...@@ -170,33 +184,33 @@ void __init native_init_IRQ(void)
* The reschedule interrupt is a CPU-to-CPU reschedule-helper * The reschedule interrupt is a CPU-to-CPU reschedule-helper
* IPI, driven by wakeup. * IPI, driven by wakeup.
*/ */
set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); alloc_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
/* IPIs for invalidation */ /* IPIs for invalidation */
set_intr_gate(INVALIDATE_TLB_VECTOR_START+0, invalidate_interrupt0); alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+0, invalidate_interrupt0);
set_intr_gate(INVALIDATE_TLB_VECTOR_START+1, invalidate_interrupt1); alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+1, invalidate_interrupt1);
set_intr_gate(INVALIDATE_TLB_VECTOR_START+2, invalidate_interrupt2); alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+2, invalidate_interrupt2);
set_intr_gate(INVALIDATE_TLB_VECTOR_START+3, invalidate_interrupt3); alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+3, invalidate_interrupt3);
set_intr_gate(INVALIDATE_TLB_VECTOR_START+4, invalidate_interrupt4); alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+4, invalidate_interrupt4);
set_intr_gate(INVALIDATE_TLB_VECTOR_START+5, invalidate_interrupt5); alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+5, invalidate_interrupt5);
set_intr_gate(INVALIDATE_TLB_VECTOR_START+6, invalidate_interrupt6); alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+6, invalidate_interrupt6);
set_intr_gate(INVALIDATE_TLB_VECTOR_START+7, invalidate_interrupt7); alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+7, invalidate_interrupt7);
/* IPI for generic function call */ /* IPI for generic function call */
set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); alloc_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
/* Low priority IPI to cleanup after moving an irq */ /* Low priority IPI to cleanup after moving an irq */
set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt); set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt);
#endif #endif
set_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt); alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
set_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt); alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt);
/* self generated IPI for local APIC timer */ /* self generated IPI for local APIC timer */
set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
/* IPI vectors for APIC spurious and error interrupts */ /* IPI vectors for APIC spurious and error interrupts */
set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
set_intr_gate(ERROR_APIC_VECTOR, error_interrupt); alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
if (!acpi_ioapic) if (!acpi_ioapic)
setup_irq(2, &irq2); setup_irq(2, &irq2);
......
...@@ -33,8 +33,7 @@ ...@@ -33,8 +33,7 @@
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/timer.h> #include <asm/timer.h>
#include <asm/i8253.h> #include <asm/i8253.h>
#include <asm/irq_vectors.h>
#include <irq_vectors.h>
#define VMI_ONESHOT (VMI_ALARM_IS_ONESHOT | VMI_CYCLES_REAL | vmi_get_alarm_wiring()) #define VMI_ONESHOT (VMI_ALARM_IS_ONESHOT | VMI_CYCLES_REAL | vmi_get_alarm_wiring())
#define VMI_PERIODIC (VMI_ALARM_IS_PERIODIC | VMI_CYCLES_REAL | vmi_get_alarm_wiring()) #define VMI_PERIODIC (VMI_ALARM_IS_PERIODIC | VMI_CYCLES_REAL | vmi_get_alarm_wiring())
......
...@@ -21,10 +21,9 @@ ...@@ -21,10 +21,9 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/i8259.h> #include <asm/i8259.h>
#include <asm/irq_vectors.h>
#include "cobalt.h" #include "cobalt.h"
#include "irq_vectors.h"
static DEFINE_SPINLOCK(cobalt_lock); static DEFINE_SPINLOCK(cobalt_lock);
......
...@@ -311,6 +311,28 @@ static inline void set_intr_gate(unsigned int n, void *addr) ...@@ -311,6 +311,28 @@ static inline void set_intr_gate(unsigned int n, void *addr)
_set_gate(n, GATE_INTERRUPT, addr, 0, 0, __KERNEL_CS); _set_gate(n, GATE_INTERRUPT, addr, 0, 0, __KERNEL_CS);
} }
#define SYS_VECTOR_FREE 0
#define SYS_VECTOR_ALLOCED 1
extern int first_system_vector;
extern char system_vectors[];
static inline void alloc_system_vector(int vector)
{
if (system_vectors[vector] == SYS_VECTOR_FREE) {
system_vectors[vector] = SYS_VECTOR_ALLOCED;
if (first_system_vector > vector)
first_system_vector = vector;
} else
BUG();
}
static inline void alloc_intr_gate(unsigned int n, void *addr)
{
alloc_system_vector(n);
set_intr_gate(n, addr);
}
/* /*
* This routine sets up an interrupt gate at directory privilege level 3. * This routine sets up an interrupt gate at directory privilege level 3.
*/ */
......
...@@ -44,4 +44,6 @@ DECLARE_PER_CPU(int, x2apic_extra_bits); ...@@ -44,4 +44,6 @@ DECLARE_PER_CPU(int, x2apic_extra_bits);
extern void uv_cpu_init(void); extern void uv_cpu_init(void);
extern int uv_wakeup_secondary(int phys_apicid, unsigned int start_rip); extern int uv_wakeup_secondary(int phys_apicid, unsigned int start_rip);
extern void setup_apic_routing(void);
#endif #endif
#ifndef _ASM_HW_IRQ_H
#define _ASM_HW_IRQ_H
/*
* (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
*
* moved some of the old arch/i386/kernel/irq.h to here. VY
*
* IRQ/IPI changes taken from work by Thomas Radke
* <tomsoft@informatik.tu-chemnitz.de>
*
* hacked by Andi Kleen for x86-64.
* unified by tglx
*/
#include <asm/irq_vectors.h>
#ifndef __ASSEMBLY__
#include <linux/percpu.h>
#include <linux/profile.h>
#include <linux/smp.h>
#include <asm/atomic.h>
#include <asm/irq.h>
#include <asm/sections.h>
#define platform_legacy_irq(irq) ((irq) < 16)
/* Interrupt handlers registered during init_IRQ */
extern void apic_timer_interrupt(void);
extern void error_interrupt(void);
extern void spurious_interrupt(void);
extern void thermal_interrupt(void);
extern void reschedule_interrupt(void);
extern void invalidate_interrupt(void);
extern void invalidate_interrupt0(void);
extern void invalidate_interrupt1(void);
extern void invalidate_interrupt2(void);
extern void invalidate_interrupt3(void);
extern void invalidate_interrupt4(void);
extern void invalidate_interrupt5(void);
extern void invalidate_interrupt6(void);
extern void invalidate_interrupt7(void);
extern void irq_move_cleanup_interrupt(void);
extern void threshold_interrupt(void);
extern void call_function_interrupt(void);
/* PIC specific functions */
extern void disable_8259A_irq(unsigned int irq);
extern void enable_8259A_irq(unsigned int irq);
extern int i8259A_irq_pending(unsigned int irq);
extern void make_8259A_irq(unsigned int irq);
extern void init_8259A(int aeoi);
/* IOAPIC */
#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs))
extern unsigned long io_apic_irqs;
extern void init_VISWS_APIC_irqs(void);
extern void setup_IO_APIC(void);
extern void disable_IO_APIC(void);
extern void print_IO_APIC(void);
extern int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
extern void setup_ioapic_dest(void);
#ifdef CONFIG_X86_64
extern void enable_IO_APIC(void);
#endif
/* IPI functions */
extern void send_IPI_self(int vector);
extern void send_IPI(int dest, int vector);
/* Statistics */
extern atomic_t irq_err_count;
extern atomic_t irq_mis_count;
/* EISA */
extern void eisa_set_level_irq(unsigned int irq);
/* Voyager functions */
extern asmlinkage void vic_cpi_interrupt(void);
extern asmlinkage void vic_sys_interrupt(void);
extern asmlinkage void vic_cmn_interrupt(void);
extern asmlinkage void qic_timer_interrupt(void);
extern asmlinkage void qic_invalidate_interrupt(void);
extern asmlinkage void qic_reschedule_interrupt(void);
extern asmlinkage void qic_enable_irq_interrupt(void);
extern asmlinkage void qic_call_function_interrupt(void);
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
# include "hw_irq_32.h" extern void (*const interrupt[NR_IRQS])(void);
#else #else
# include "hw_irq_64.h" typedef int vector_irq_t[NR_VECTORS];
DECLARE_PER_CPU(vector_irq_t, vector_irq);
extern void __setup_vector_irq(int cpu);
extern spinlock_t vector_lock;
#endif
#endif /* !ASSEMBLY_ */
#endif #endif
#ifndef _ASM_HW_IRQ_H
#define _ASM_HW_IRQ_H
/*
* linux/include/asm/hw_irq.h
*
* (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
*
* moved some of the old arch/i386/kernel/irq.h to here. VY
*
* IRQ/IPI changes taken from work by Thomas Radke
* <tomsoft@informatik.tu-chemnitz.de>
*/
#include <linux/profile.h>
#include <asm/atomic.h>
#include <asm/irq.h>
#include <asm/sections.h>
#define NMI_VECTOR 0x02
/*
* Various low-level irq details needed by irq.c, process.c,
* time.c, io_apic.c and smp.c
*
* Interrupt entry/exit code at both C and assembly level
*/
extern void (*const interrupt[NR_IRQS])(void);
#ifdef CONFIG_SMP
void reschedule_interrupt(void);
void invalidate_interrupt(void);
void call_function_interrupt(void);
#endif
#ifdef CONFIG_X86_LOCAL_APIC
void apic_timer_interrupt(void);
void error_interrupt(void);
void spurious_interrupt(void);
void thermal_interrupt(void);
#define platform_legacy_irq(irq) ((irq) < 16)
#endif
void disable_8259A_irq(unsigned int irq);
void enable_8259A_irq(unsigned int irq);
int i8259A_irq_pending(unsigned int irq);
void make_8259A_irq(unsigned int irq);
void init_8259A(int aeoi);
void send_IPI_self(int vector);
void init_VISWS_APIC_irqs(void);
void setup_IO_APIC(void);
void disable_IO_APIC(void);
void print_IO_APIC(void);
int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
void send_IPI(int dest, int vector);
void setup_ioapic_dest(void);
extern unsigned long io_apic_irqs;
extern atomic_t irq_err_count;
extern atomic_t irq_mis_count;
#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs))
#endif /* _ASM_HW_IRQ_H */
#ifndef _ASM_HW_IRQ_H
#define _ASM_HW_IRQ_H
/*
* linux/include/asm/hw_irq.h
*
* (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
*
* moved some of the old arch/i386/kernel/irq.h to here. VY
*
* IRQ/IPI changes taken from work by Thomas Radke
* <tomsoft@informatik.tu-chemnitz.de>
*
* hacked by Andi Kleen for x86-64.
*/
#ifndef __ASSEMBLY__
#include <asm/atomic.h>
#include <asm/irq.h>
#include <linux/profile.h>
#include <linux/smp.h>
#include <linux/percpu.h>
#endif
#define NMI_VECTOR 0x02
/*
* IDT vectors usable for external interrupt sources start
* at 0x20:
*/
#define FIRST_EXTERNAL_VECTOR 0x20
#define IA32_SYSCALL_VECTOR 0x80
/* Reserve the lowest usable priority level 0x20 - 0x2f for triggering
* cleanup after irq migration.
*/
#define IRQ_MOVE_CLEANUP_VECTOR FIRST_EXTERNAL_VECTOR
/*
* Vectors 0x30-0x3f are used for ISA interrupts.
*/
#define IRQ0_VECTOR (FIRST_EXTERNAL_VECTOR + 0x10)
#define IRQ1_VECTOR (IRQ0_VECTOR + 1)
#define IRQ2_VECTOR (IRQ0_VECTOR + 2)
#define IRQ3_VECTOR (IRQ0_VECTOR + 3)
#define IRQ4_VECTOR (IRQ0_VECTOR + 4)
#define IRQ5_VECTOR (IRQ0_VECTOR + 5)
#define IRQ6_VECTOR (IRQ0_VECTOR + 6)
#define IRQ7_VECTOR (IRQ0_VECTOR + 7)
#define IRQ8_VECTOR (IRQ0_VECTOR + 8)
#define IRQ9_VECTOR (IRQ0_VECTOR + 9)
#define IRQ10_VECTOR (IRQ0_VECTOR + 10)
#define IRQ11_VECTOR (IRQ0_VECTOR + 11)
#define IRQ12_VECTOR (IRQ0_VECTOR + 12)
#define IRQ13_VECTOR (IRQ0_VECTOR + 13)
#define IRQ14_VECTOR (IRQ0_VECTOR + 14)
#define IRQ15_VECTOR (IRQ0_VECTOR + 15)
/*
* Special IRQ vectors used by the SMP architecture, 0xf0-0xff
*
* some of the following vectors are 'rare', they are merged
* into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
* TLB, reschedule and local APIC vectors are performance-critical.
*/
#define SPURIOUS_APIC_VECTOR 0xff
#define ERROR_APIC_VECTOR 0xfe
#define RESCHEDULE_VECTOR 0xfd
#define CALL_FUNCTION_VECTOR 0xfc
/* fb free - please don't readd KDB here because it's useless
(hint - think what a NMI bit does to a vector) */
#define THERMAL_APIC_VECTOR 0xfa
#define THRESHOLD_APIC_VECTOR 0xf9
/* f8 free */
#define INVALIDATE_TLB_VECTOR_END 0xf7
#define INVALIDATE_TLB_VECTOR_START 0xf0 /* f0-f7 used for TLB flush */
#define NUM_INVALIDATE_TLB_VECTORS 8
/*
* Local APIC timer IRQ vector is on a different priority level,
* to work around the 'lost local interrupt if more than 2 IRQ
* sources per level' errata.
*/
#define LOCAL_TIMER_VECTOR 0xef
/*
* First APIC vector available to drivers: (vectors 0x30-0xee)
* we start at 0x41 to spread out vectors evenly between priority
* levels. (0x80 is the syscall vector)
*/
#define FIRST_DEVICE_VECTOR (IRQ15_VECTOR + 2)
#define FIRST_SYSTEM_VECTOR 0xef /* duplicated in irq.h */
#ifndef __ASSEMBLY__
/* Interrupt handlers registered during init_IRQ */
void apic_timer_interrupt(void);
void spurious_interrupt(void);
void error_interrupt(void);
void reschedule_interrupt(void);
void call_function_interrupt(void);
void irq_move_cleanup_interrupt(void);
void invalidate_interrupt0(void);
void invalidate_interrupt1(void);
void invalidate_interrupt2(void);
void invalidate_interrupt3(void);
void invalidate_interrupt4(void);
void invalidate_interrupt5(void);
void invalidate_interrupt6(void);
void invalidate_interrupt7(void);
void thermal_interrupt(void);
void threshold_interrupt(void);
void i8254_timer_resume(void);
typedef int vector_irq_t[NR_VECTORS];
DECLARE_PER_CPU(vector_irq_t, vector_irq);
extern void __setup_vector_irq(int cpu);
extern spinlock_t vector_lock;
/*
* Various low-level irq details needed by irq.c, process.c,
* time.c, io_apic.c and smp.c
*
* Interrupt entry/exit code at both C and assembly level
*/
extern void disable_8259A_irq(unsigned int irq);
extern void enable_8259A_irq(unsigned int irq);
extern int i8259A_irq_pending(unsigned int irq);
extern void make_8259A_irq(unsigned int irq);
extern void init_8259A(int aeoi);
extern void send_IPI_self(int vector);
extern void init_VISWS_APIC_irqs(void);
extern void setup_IO_APIC(void);
extern void enable_IO_APIC(void);
extern void disable_IO_APIC(void);
extern void print_IO_APIC(void);
extern int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
extern void send_IPI(int dest, int vector);
extern void setup_ioapic_dest(void);
extern void native_init_IRQ(void);
extern unsigned long io_apic_irqs;
extern atomic_t irq_err_count;
extern atomic_t irq_mis_count;
#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs))
#include <asm/ptrace.h>
#define IRQ_NAME2(nr) nr##_interrupt(void)
#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
/*
* SMP has a few special interrupts for IPI messages
*/
#define BUILD_IRQ(nr) \
asmlinkage void IRQ_NAME(nr); \
asm("\n.p2align\n" \
"IRQ" #nr "_interrupt:\n\t" \
"push $~(" #nr ") ; " \
"jmp common_interrupt");
#define platform_legacy_irq(irq) ((irq) < 16)
#endif
#endif /* _ASM_HW_IRQ_H */
#ifdef CONFIG_X86_32 #ifndef _ASM_IRQ_H
# include "irq_32.h" #define _ASM_IRQ_H
/*
* (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
*
* IRQ/IPI changes taken from work by Thomas Radke
* <tomsoft@informatik.tu-chemnitz.de>
*/
#include <asm/apicdef.h>
#include <asm/irq_vectors.h>
static inline int irq_canonicalize(int irq)
{
return ((irq == 2) ? 9 : irq);
}
#ifdef CONFIG_X86_LOCAL_APIC
# define ARCH_HAS_NMI_WATCHDOG
#endif
#ifdef CONFIG_4KSTACKS
extern void irq_ctx_init(int cpu);
extern void irq_ctx_exit(int cpu);
# define __ARCH_HAS_DO_SOFTIRQ
#else #else
# include "irq_64.h" # define irq_ctx_init(cpu) do { } while (0)
# define irq_ctx_exit(cpu) do { } while (0)
# ifdef CONFIG_X86_64
# define __ARCH_HAS_DO_SOFTIRQ
# endif
#endif
#ifdef CONFIG_IRQBALANCE
extern int irqbalance_disable(char *str);
#endif
#ifdef CONFIG_HOTPLUG_CPU
#include <linux/cpumask.h>
extern void fixup_irqs(cpumask_t map);
#endif #endif
extern unsigned int do_IRQ(struct pt_regs *regs);
extern void init_IRQ(void);
extern void native_init_IRQ(void);
/* Interrupt vector management */
extern DECLARE_BITMAP(used_vectors, NR_VECTORS);
#endif /* _ASM_IRQ_H */
#ifndef _ASM_IRQ_H
#define _ASM_IRQ_H
/*
* linux/include/asm/irq.h
*
* (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
*
* IRQ/IPI changes taken from work by Thomas Radke
* <tomsoft@informatik.tu-chemnitz.de>
*/
#include <linux/sched.h>
/* include comes from machine specific directory */
#include "irq_vectors.h"
#include <asm/thread_info.h>
static inline int irq_canonicalize(int irq)
{
return ((irq == 2) ? 9 : irq);
}
#ifdef CONFIG_X86_LOCAL_APIC
# define ARCH_HAS_NMI_WATCHDOG /* See include/linux/nmi.h */
#endif
#ifdef CONFIG_4KSTACKS
extern void irq_ctx_init(int cpu);
extern void irq_ctx_exit(int cpu);
# define __ARCH_HAS_DO_SOFTIRQ
#else
# define irq_ctx_init(cpu) do { } while (0)
# define irq_ctx_exit(cpu) do { } while (0)
#endif
#ifdef CONFIG_IRQBALANCE
extern int irqbalance_disable(char *str);
#endif
#ifdef CONFIG_HOTPLUG_CPU
extern void fixup_irqs(cpumask_t map);
#endif
unsigned int do_IRQ(struct pt_regs *regs);
void init_IRQ(void);
void __init native_init_IRQ(void);
/* Interrupt vector management */
extern DECLARE_BITMAP(used_vectors, NR_VECTORS);
#endif /* _ASM_IRQ_H */
#ifndef _ASM_IRQ_H
#define _ASM_IRQ_H
/*
* linux/include/asm/irq.h
*
* (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
*
* IRQ/IPI changes taken from work by Thomas Radke
* <tomsoft@informatik.tu-chemnitz.de>
*/
#define TIMER_IRQ 0
/*
* 16 8259A IRQ's, 208 potential APIC interrupt sources.
* Right now the APIC is mostly only used for SMP.
* 256 vectors is an architectural limit. (we can have
* more than 256 devices theoretically, but they will
* have to use shared interrupts)
* Since vectors 0x00-0x1f are used/reserved for the CPU,
* the usable vector space is 0x20-0xff (224 vectors)
*/
/*
* The maximum number of vectors supported by x86_64 processors
* is limited to 256. For processors other than x86_64, NR_VECTORS
* should be changed accordingly.
*/
#define NR_VECTORS 256
#define FIRST_SYSTEM_VECTOR 0xef /* duplicated in hw_irq.h */
#define NR_IRQS (NR_VECTORS + (32 * NR_CPUS))
#define NR_IRQ_VECTORS NR_IRQS
static inline int irq_canonicalize(int irq)
{
return ((irq == 2) ? 9 : irq);
}
#define ARCH_HAS_NMI_WATCHDOG /* See include/linux/nmi.h */
#ifdef CONFIG_HOTPLUG_CPU
#include <linux/cpumask.h>
extern void fixup_irqs(cpumask_t map);
#endif
#define __ARCH_HAS_DO_SOFTIRQ 1
#endif /* _ASM_IRQ_H */
/* -*- mode: c; c-basic-offset: 8 -*- */
/* Copyright (C) 2002
*
* Author: James.Bottomley@HansenPartnership.com
*
* linux/arch/i386/voyager/irq_vectors.h
*
* This file provides definitions for the VIC and QIC CPIs
*/
#ifndef _ASM_IRQ_VECTORS_H #ifndef _ASM_IRQ_VECTORS_H
#define _ASM_IRQ_VECTORS_H #define _ASM_IRQ_VECTORS_H
#include <linux/threads.h>
#define NMI_VECTOR 0x02
/* /*
* IDT vectors usable for external interrupt sources start * IDT vectors usable for external interrupt sources start
* at 0x20: * at 0x20:
*/ */
#define FIRST_EXTERNAL_VECTOR 0x20 #define FIRST_EXTERNAL_VECTOR 0x20
#define SYSCALL_VECTOR 0x80 #ifdef CONFIG_X86_32
# define SYSCALL_VECTOR 0x80
#else
# define IA32_SYSCALL_VECTOR 0x80
#endif
/*
* Reserve the lowest usable priority level 0x20 - 0x2f for triggering
* cleanup after irq migration on 64 bit.
*/
#define IRQ_MOVE_CLEANUP_VECTOR FIRST_EXTERNAL_VECTOR
/* /*
* Vectors 0x20-0x2f are used for ISA interrupts. * Vectors 0x20-0x2f are used for ISA interrupts on 32 bit.
* Vectors 0x30-0x3f are used for ISA interrupts on 64 bit.
*/ */
#ifdef CONFIG_X86_32
#define IRQ0_VECTOR (FIRST_EXTERNAL_VECTOR)
#else
#define IRQ0_VECTOR (FIRST_EXTERNAL_VECTOR + 0x10)
#endif
#define IRQ1_VECTOR (IRQ0_VECTOR + 1)
#define IRQ2_VECTOR (IRQ0_VECTOR + 2)
#define IRQ3_VECTOR (IRQ0_VECTOR + 3)
#define IRQ4_VECTOR (IRQ0_VECTOR + 4)
#define IRQ5_VECTOR (IRQ0_VECTOR + 5)
#define IRQ6_VECTOR (IRQ0_VECTOR + 6)
#define IRQ7_VECTOR (IRQ0_VECTOR + 7)
#define IRQ8_VECTOR (IRQ0_VECTOR + 8)
#define IRQ9_VECTOR (IRQ0_VECTOR + 9)
#define IRQ10_VECTOR (IRQ0_VECTOR + 10)
#define IRQ11_VECTOR (IRQ0_VECTOR + 11)
#define IRQ12_VECTOR (IRQ0_VECTOR + 12)
#define IRQ13_VECTOR (IRQ0_VECTOR + 13)
#define IRQ14_VECTOR (IRQ0_VECTOR + 14)
#define IRQ15_VECTOR (IRQ0_VECTOR + 15)
/*
* Special IRQ vectors used by the SMP architecture, 0xf0-0xff
*
* some of the following vectors are 'rare', they are merged
* into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
* TLB, reschedule and local APIC vectors are performance-critical.
*
* Vectors 0xf0-0xfa are free (reserved for future Linux use).
*/
#ifdef CONFIG_X86_32
# define SPURIOUS_APIC_VECTOR 0xff
# define ERROR_APIC_VECTOR 0xfe
# define INVALIDATE_TLB_VECTOR 0xfd
# define RESCHEDULE_VECTOR 0xfc
# define CALL_FUNCTION_VECTOR 0xfb
# define THERMAL_APIC_VECTOR 0xf0
#else
#define SPURIOUS_APIC_VECTOR 0xff
#define ERROR_APIC_VECTOR 0xfe
#define RESCHEDULE_VECTOR 0xfd
#define CALL_FUNCTION_VECTOR 0xfc
#define THERMAL_APIC_VECTOR 0xfa
#define THRESHOLD_APIC_VECTOR 0xf9
#define INVALIDATE_TLB_VECTOR_END 0xf7
#define INVALIDATE_TLB_VECTOR_START 0xf0 /* f0-f7 used for TLB flush */
#define NUM_INVALIDATE_TLB_VECTORS 8
#endif
/*
* Local APIC timer IRQ vector is on a different priority level,
* to work around the 'lost local interrupt if more than 2 IRQ
* sources per level' errata.
*/
#define LOCAL_TIMER_VECTOR 0xef
/*
* First APIC vector available to drivers: (vectors 0x30-0xee) we
* start at 0x31(0x41) to spread out vectors evenly between priority
* levels. (0x80 is the syscall vector)
*/
#ifdef CONFIG_X86_32
# define FIRST_DEVICE_VECTOR 0x31
#else
# define FIRST_DEVICE_VECTOR (IRQ15_VECTOR + 2)
#endif
#define NR_VECTORS 256
#define FPU_IRQ 13
#define FIRST_VM86_IRQ 3
#define LAST_VM86_IRQ 15
#define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15)
#if !defined(CONFIG_X86_VISWS) && !defined(CONFIG_X86_VOYAGER)
# if defined(CONFIG_X86_IO_APIC) || defined(CONFIG_PARAVIRT)
# define NR_IRQS 224
# if (224 >= 32 * NR_CPUS)
# define NR_IRQ_VECTORS NR_IRQS
# else
# define NR_IRQ_VECTORS (32 * NR_CPUS)
# endif
# else /* IO_APIC || PARAVIRT */
# define NR_IRQS 16
# define NR_IRQ_VECTORS NR_IRQS
# endif
#else /* !VISWS && !VOYAGER */
# define NR_IRQS 224
# define NR_IRQ_VECTORS NR_IRQS
#endif /* VISWS */
/* Voyager specific defines */
/* These define the CPIs we use in linux */ /* These define the CPIs we use in linux */
#define VIC_CPI_LEVEL0 0 #define VIC_CPI_LEVEL0 0
#define VIC_CPI_LEVEL1 1 #define VIC_CPI_LEVEL1 1
...@@ -55,25 +165,5 @@ ...@@ -55,25 +165,5 @@
#define VIC_CPU_BOOT_CPI VIC_CPI_LEVEL0 #define VIC_CPU_BOOT_CPI VIC_CPI_LEVEL0
#define VIC_CPU_BOOT_ERRATA_CPI (VIC_CPI_LEVEL0 + 8) #define VIC_CPU_BOOT_ERRATA_CPI (VIC_CPI_LEVEL0 + 8)
#define NR_VECTORS 256
#define NR_IRQS 224
#define NR_IRQ_VECTORS NR_IRQS
#define FPU_IRQ 13
#define FIRST_VM86_IRQ 3
#define LAST_VM86_IRQ 15
#define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15)
#ifndef __ASSEMBLY__
extern asmlinkage void vic_cpi_interrupt(void);
extern asmlinkage void vic_sys_interrupt(void);
extern asmlinkage void vic_cmn_interrupt(void);
extern asmlinkage void qic_timer_interrupt(void);
extern asmlinkage void qic_invalidate_interrupt(void);
extern asmlinkage void qic_reschedule_interrupt(void);
extern asmlinkage void qic_enable_irq_interrupt(void);
extern asmlinkage void qic_call_function_interrupt(void);
#endif /* !__ASSEMBLY__ */
#endif /* _ASM_IRQ_VECTORS_H */ #endif /* _ASM_IRQ_VECTORS_H */
/*
* This file should contain #defines for all of the interrupt vector
* numbers used by this architecture.
*
* In addition, there are some standard defines:
*
* FIRST_EXTERNAL_VECTOR:
* The first free place for external interrupts
*
* SYSCALL_VECTOR:
* The IRQ vector a syscall makes the user to kernel transition
* under.
*
* TIMER_IRQ:
* The IRQ number the timer interrupt comes in at.
*
* NR_IRQS:
* The total number of interrupt vectors (including all the
* architecture specific interrupts) needed.
*
*/
#ifndef _ASM_IRQ_VECTORS_H
#define _ASM_IRQ_VECTORS_H
/*
* IDT vectors usable for external interrupt sources start
* at 0x20:
*/
#define FIRST_EXTERNAL_VECTOR 0x20
#define SYSCALL_VECTOR 0x80
/*
* Vectors 0x20-0x2f are used for ISA interrupts.
*/
/*
* Special IRQ vectors used by the SMP architecture, 0xf0-0xff
*
* some of the following vectors are 'rare', they are merged
* into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
* TLB, reschedule and local APIC vectors are performance-critical.
*
* Vectors 0xf0-0xfa are free (reserved for future Linux use).
*/
#define SPURIOUS_APIC_VECTOR 0xff
#define ERROR_APIC_VECTOR 0xfe
#define INVALIDATE_TLB_VECTOR 0xfd
#define RESCHEDULE_VECTOR 0xfc
#define CALL_FUNCTION_VECTOR 0xfb
#define THERMAL_APIC_VECTOR 0xf0
/*
* Local APIC timer IRQ vector is on a different priority level,
* to work around the 'lost local interrupt if more than 2 IRQ
* sources per level' errata.
*/
#define LOCAL_TIMER_VECTOR 0xef
/*
* First APIC vector available to drivers: (vectors 0x30-0xee)
* we start at 0x31 to spread out vectors evenly between priority
* levels. (0x80 is the syscall vector)
*/
#define FIRST_DEVICE_VECTOR 0x31
#define FIRST_SYSTEM_VECTOR 0xef
#define TIMER_IRQ 0
/*
* 16 8259A IRQ's, 208 potential APIC interrupt sources.
* Right now the APIC is mostly only used for SMP.
* 256 vectors is an architectural limit. (we can have
* more than 256 devices theoretically, but they will
* have to use shared interrupts)
* Since vectors 0x00-0x1f are used/reserved for the CPU,
* the usable vector space is 0x20-0xff (224 vectors)
*/
/*
* The maximum number of vectors supported by i386 processors
* is limited to 256. For processors other than i386, NR_VECTORS
* should be changed accordingly.
*/
#define NR_VECTORS 256
#include "irq_vectors_limits.h"
#define FPU_IRQ 13
#define FIRST_VM86_IRQ 3
#define LAST_VM86_IRQ 15
#define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15)
#endif /* _ASM_IRQ_VECTORS_H */
#ifndef _ASM_IRQ_VECTORS_LIMITS_H
#define _ASM_IRQ_VECTORS_LIMITS_H
#if defined(CONFIG_X86_IO_APIC) || defined(CONFIG_PARAVIRT)
#define NR_IRQS 224
# if (224 >= 32 * NR_CPUS)
# define NR_IRQ_VECTORS NR_IRQS
# else
# define NR_IRQ_VECTORS (32 * NR_CPUS)
# endif
#else
#define NR_IRQS 16
#define NR_IRQ_VECTORS NR_IRQS
#endif
#endif /* _ASM_IRQ_VECTORS_LIMITS_H */
#ifndef _ASM_IRQ_VECTORS_H
#define _ASM_IRQ_VECTORS_H
/*
* IDT vectors usable for external interrupt sources start
* at 0x20:
*/
#define FIRST_EXTERNAL_VECTOR 0x20
#define SYSCALL_VECTOR 0x80
/*
* Vectors 0x20-0x2f are used for ISA interrupts.
*/
/*
* Special IRQ vectors used by the SMP architecture, 0xf0-0xff
*
* some of the following vectors are 'rare', they are merged
* into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
* TLB, reschedule and local APIC vectors are performance-critical.
*
* Vectors 0xf0-0xfa are free (reserved for future Linux use).
*/
#define SPURIOUS_APIC_VECTOR 0xff
#define ERROR_APIC_VECTOR 0xfe
#define INVALIDATE_TLB_VECTOR 0xfd
#define RESCHEDULE_VECTOR 0xfc
#define CALL_FUNCTION_VECTOR 0xfb
#define THERMAL_APIC_VECTOR 0xf0
/*
* Local APIC timer IRQ vector is on a different priority level,
* to work around the 'lost local interrupt if more than 2 IRQ
* sources per level' errata.
*/
#define LOCAL_TIMER_VECTOR 0xef
/*
* First APIC vector available to drivers: (vectors 0x30-0xee)
* we start at 0x31 to spread out vectors evenly between priority
* levels. (0x80 is the syscall vector)
*/
#define FIRST_DEVICE_VECTOR 0x31
#define FIRST_SYSTEM_VECTOR 0xef
#define TIMER_IRQ 0
/*
* IRQ definitions
*/
#define NR_VECTORS 256
#define NR_IRQS 224
#define NR_IRQ_VECTORS NR_IRQS
#define FPU_IRQ 13
#define FIRST_VM86_IRQ 3
#define LAST_VM86_IRQ 15
#define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15)
#endif /* _ASM_IRQ_VECTORS_H */
This diff is collapsed.
This diff is collapsed.
#ifndef _LINUX_KERNEL_STAT_H #ifndef _LINUX_KERNEL_STAT_H
#define _LINUX_KERNEL_STAT_H #define _LINUX_KERNEL_STAT_H
#include <asm/irq.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/threads.h> #include <linux/threads.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <asm/irq.h>
#include <asm/cputime.h> #include <asm/cputime.h>
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment