Commit a77d2e08 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'x86-apic-for-linus' of...

Merge branch 'x86-apic-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'x86-apic-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (30 commits)
  x86, apic: Enable lapic nmi watchdog on AMD Family 11h
  x86: Remove unnecessary mdelay() from cpu_disable_common()
  x86, ioapic: Document another case when level irq is seen as an edge
  x86, ioapic: Fix the EOI register detection mechanism
  x86, io-apic: Move the effort of clearing remoteIRR explicitly before migrating the irq
  x86: SGI UV: Map low MMR ranges
  x86: apic: Print out SRAT table APIC id in hex
  x86: Re-get cfg_new in case reuse/move irq_desc
  x86: apic: Remove not needed #ifdef
  x86: io-apic: IO-APIC MMIO should not fail on resource insertion
  x86: Remove asm/apicnum.h
  x86: apic: Do not use stacked physid_mask_t
  x86, apic: Get rid of apicid_to_cpu_present assign on 64-bit
  x86, ioapic: Use snrpintf while set names for IO-APIC resourses
  x86, apic: Use PAGE_SIZE instead of numbers
  x86: Remove local_irq_enable()/local_irq_disable() in fixup_irqs()
  x86: Use EOI register in io-apic on intel platforms
  x86: Force irq complete move during cpu offline
  x86: Remove move_cleanup_count from irq_cfg
  x86, intr-remap: Avoid irq_chip mask/unmask in fixup_irqs() for intr-remapping
  ...
parents 897e81be 7d1849af
...@@ -344,6 +344,15 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -344,6 +344,15 @@ and is between 256 and 4096 characters. It is defined in the file
Change the amount of debugging information output Change the amount of debugging information output
when initialising the APIC and IO-APIC components. when initialising the APIC and IO-APIC components.
show_lapic= [APIC,X86] Advanced Programmable Interrupt Controller
Limit apic dumping. The parameter defines the maximal
number of local apics being dumped. Also it is possible
to set it to "all" by meaning -- no limit here.
Format: { 1 (default) | 2 | ... | all }.
The parameter valid if only apic=debug or
apic=verbose is specified.
Example: apic=debug show_lapic=all
apm= [APM] Advanced Power Management apm= [APM] Advanced Power Management
See header of arch/x86/kernel/apm_32.c. See header of arch/x86/kernel/apm_32.c.
......
...@@ -297,20 +297,20 @@ struct apic { ...@@ -297,20 +297,20 @@ struct apic {
int disable_esr; int disable_esr;
int dest_logical; int dest_logical;
unsigned long (*check_apicid_used)(physid_mask_t bitmap, int apicid); unsigned long (*check_apicid_used)(physid_mask_t *map, int apicid);
unsigned long (*check_apicid_present)(int apicid); unsigned long (*check_apicid_present)(int apicid);
void (*vector_allocation_domain)(int cpu, struct cpumask *retmask); void (*vector_allocation_domain)(int cpu, struct cpumask *retmask);
void (*init_apic_ldr)(void); void (*init_apic_ldr)(void);
physid_mask_t (*ioapic_phys_id_map)(physid_mask_t map); void (*ioapic_phys_id_map)(physid_mask_t *phys_map, physid_mask_t *retmap);
void (*setup_apic_routing)(void); void (*setup_apic_routing)(void);
int (*multi_timer_check)(int apic, int irq); int (*multi_timer_check)(int apic, int irq);
int (*apicid_to_node)(int logical_apicid); int (*apicid_to_node)(int logical_apicid);
int (*cpu_to_logical_apicid)(int cpu); int (*cpu_to_logical_apicid)(int cpu);
int (*cpu_present_to_apicid)(int mps_cpu); int (*cpu_present_to_apicid)(int mps_cpu);
physid_mask_t (*apicid_to_cpu_present)(int phys_apicid); void (*apicid_to_cpu_present)(int phys_apicid, physid_mask_t *retmap);
void (*setup_portio_remap)(void); void (*setup_portio_remap)(void);
int (*check_phys_apicid_present)(int phys_apicid); int (*check_phys_apicid_present)(int phys_apicid);
void (*enable_apic_mode)(void); void (*enable_apic_mode)(void);
...@@ -488,6 +488,8 @@ static inline unsigned int read_apic_id(void) ...@@ -488,6 +488,8 @@ static inline unsigned int read_apic_id(void)
extern void default_setup_apic_routing(void); extern void default_setup_apic_routing(void);
extern struct apic apic_noop;
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
extern struct apic apic_default; extern struct apic apic_default;
...@@ -532,9 +534,9 @@ default_cpu_mask_to_apicid_and(const struct cpumask *cpumask, ...@@ -532,9 +534,9 @@ default_cpu_mask_to_apicid_and(const struct cpumask *cpumask,
return (unsigned int)(mask1 & mask2 & mask3); return (unsigned int)(mask1 & mask2 & mask3);
} }
static inline unsigned long default_check_apicid_used(physid_mask_t bitmap, int apicid) static inline unsigned long default_check_apicid_used(physid_mask_t *map, int apicid)
{ {
return physid_isset(apicid, bitmap); return physid_isset(apicid, *map);
} }
static inline unsigned long default_check_apicid_present(int bit) static inline unsigned long default_check_apicid_present(int bit)
...@@ -542,9 +544,9 @@ static inline unsigned long default_check_apicid_present(int bit) ...@@ -542,9 +544,9 @@ static inline unsigned long default_check_apicid_present(int bit)
return physid_isset(bit, phys_cpu_present_map); return physid_isset(bit, phys_cpu_present_map);
} }
static inline physid_mask_t default_ioapic_phys_id_map(physid_mask_t phys_map) static inline void default_ioapic_phys_id_map(physid_mask_t *phys_map, physid_mask_t *retmap)
{ {
return phys_map; *retmap = *phys_map;
} }
/* Mapping from cpu number to logical apicid */ /* Mapping from cpu number to logical apicid */
...@@ -583,11 +585,6 @@ extern int default_cpu_present_to_apicid(int mps_cpu); ...@@ -583,11 +585,6 @@ extern int default_cpu_present_to_apicid(int mps_cpu);
extern int default_check_phys_apicid_present(int phys_apicid); extern int default_check_phys_apicid_present(int phys_apicid);
#endif #endif
static inline physid_mask_t default_apicid_to_cpu_present(int phys_apicid)
{
return physid_mask_of_physid(phys_apicid);
}
#endif /* CONFIG_X86_LOCAL_APIC */ #endif /* CONFIG_X86_LOCAL_APIC */
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
......
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
#define IO_APIC_DEFAULT_PHYS_BASE 0xfec00000 #define IO_APIC_DEFAULT_PHYS_BASE 0xfec00000
#define APIC_DEFAULT_PHYS_BASE 0xfee00000 #define APIC_DEFAULT_PHYS_BASE 0xfee00000
/*
* This is the IO-APIC register space as specified
* by Intel docs:
*/
#define IO_APIC_SLOT_SIZE 1024
#define APIC_ID 0x20 #define APIC_ID 0x20
#define APIC_LVR 0x30 #define APIC_LVR 0x30
......
#ifndef _ASM_X86_APICNUM_H
#define _ASM_X86_APICNUM_H
/* define MAX_IO_APICS */
#ifdef CONFIG_X86_32
# define MAX_IO_APICS 64
#else
# define MAX_IO_APICS 128
# define MAX_LOCAL_APIC 32768
#endif
#endif /* _ASM_X86_APICNUM_H */
...@@ -85,8 +85,26 @@ static inline void set_io_apic_irq_attr(struct io_apic_irq_attr *irq_attr, ...@@ -85,8 +85,26 @@ static inline void set_io_apic_irq_attr(struct io_apic_irq_attr *irq_attr,
irq_attr->polarity = polarity; irq_attr->polarity = polarity;
} }
extern int IO_APIC_get_PCI_irq_vector(int bus, int devfn, int pin, /*
struct io_apic_irq_attr *irq_attr); * This is performance-critical, we want to do it O(1)
*
* Most irqs are mapped 1:1 with pins.
*/
struct irq_cfg {
struct irq_pin_list *irq_2_pin;
cpumask_var_t domain;
cpumask_var_t old_domain;
u8 vector;
u8 move_in_progress : 1;
};
extern struct irq_cfg *irq_cfg(unsigned int);
extern int assign_irq_vector(int, struct irq_cfg *, const struct cpumask *);
extern void send_cleanup_vector(struct irq_cfg *);
struct irq_desc;
extern unsigned int set_desc_affinity(struct irq_desc *, const struct cpumask *);
extern int IO_APIC_get_PCI_irq_vector(int bus, int devfn, int pin, struct io_apic_irq_attr *irq_attr);
extern void setup_ioapic_dest(void); extern void setup_ioapic_dest(void);
extern void enable_IO_APIC(void); extern void enable_IO_APIC(void);
......
...@@ -34,6 +34,7 @@ static inline int irq_canonicalize(int irq) ...@@ -34,6 +34,7 @@ static inline int irq_canonicalize(int irq)
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
#include <linux/cpumask.h> #include <linux/cpumask.h>
extern void fixup_irqs(void); extern void fixup_irqs(void);
extern void irq_force_complete_move(int);
#endif #endif
extern void (*generic_interrupt_extension)(void); extern void (*generic_interrupt_extension)(void);
......
...@@ -163,14 +163,16 @@ typedef struct physid_mask physid_mask_t; ...@@ -163,14 +163,16 @@ typedef struct physid_mask physid_mask_t;
#define physids_shift_left(d, s, n) \ #define physids_shift_left(d, s, n) \
bitmap_shift_left((d).mask, (s).mask, n, MAX_APICS) bitmap_shift_left((d).mask, (s).mask, n, MAX_APICS)
#define physids_coerce(map) ((map).mask[0]) static inline unsigned long physids_coerce(physid_mask_t *map)
{
return map->mask[0];
}
#define physids_promote(physids) \ static inline void physids_promote(unsigned long physids, physid_mask_t *map)
({ \ {
physid_mask_t __physid_mask = PHYSID_MASK_NONE; \ physids_clear(*map);
__physid_mask.mask[0] = physids; \ map->mask[0] = physids;
__physid_mask; \ }
})
/* Note: will create very large stack frames if physid_mask_t is big */ /* Note: will create very large stack frames if physid_mask_t is big */
#define physid_mask_of_physid(physid) \ #define physid_mask_of_physid(physid) \
......
...@@ -25,12 +25,14 @@ struct uv_IO_APIC_route_entry { ...@@ -25,12 +25,14 @@ struct uv_IO_APIC_route_entry {
dest : 32; dest : 32;
}; };
extern struct irq_chip uv_irq_chip; enum {
UV_AFFINITY_ALL,
extern int arch_enable_uv_irq(char *, unsigned int, int, int, unsigned long); UV_AFFINITY_NODE,
extern void arch_disable_uv_irq(int, unsigned long); UV_AFFINITY_CPU
};
extern int uv_setup_irq(char *, int, int, unsigned long); extern int uv_irq_2_mmr_info(int, unsigned long *, int *);
extern void uv_teardown_irq(unsigned int, int, unsigned long); extern int uv_setup_irq(char *, int, int, unsigned long, int);
extern void uv_teardown_irq(unsigned int);
#endif /* _ASM_X86_UV_UV_IRQ_H */ #endif /* _ASM_X86_UV_UV_IRQ_H */
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Makefile for local APIC drivers and for the IO-APIC code # Makefile for local APIC drivers and for the IO-APIC code
# #
obj-$(CONFIG_X86_LOCAL_APIC) += apic.o probe_$(BITS).o ipi.o nmi.o obj-$(CONFIG_X86_LOCAL_APIC) += apic.o apic_noop.o probe_$(BITS).o ipi.o nmi.o
obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o
obj-$(CONFIG_SMP) += ipi.o obj-$(CONFIG_SMP) += ipi.o
......
...@@ -241,28 +241,13 @@ static int modern_apic(void) ...@@ -241,28 +241,13 @@ static int modern_apic(void)
} }
/* /*
* bare function to substitute write operation * right after this call apic become NOOP driven
* and it's _that_ fast :) * so apic->write/read doesn't do anything
*/
static void native_apic_write_dummy(u32 reg, u32 v)
{
WARN_ON_ONCE((cpu_has_apic || !disable_apic));
}
static u32 native_apic_read_dummy(u32 reg)
{
WARN_ON_ONCE((cpu_has_apic && !disable_apic));
return 0;
}
/*
* right after this call apic->write/read doesn't do anything
* note that there is no restore operation it works one way
*/ */
void apic_disable(void) void apic_disable(void)
{ {
apic->read = native_apic_read_dummy; pr_info("APIC: switched to apic NOOP\n");
apic->write = native_apic_write_dummy; apic = &apic_noop;
} }
void native_apic_wait_icr_idle(void) void native_apic_wait_icr_idle(void)
...@@ -459,7 +444,7 @@ static void lapic_timer_setup(enum clock_event_mode mode, ...@@ -459,7 +444,7 @@ static void lapic_timer_setup(enum clock_event_mode mode,
v = apic_read(APIC_LVTT); v = apic_read(APIC_LVTT);
v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR);
apic_write(APIC_LVTT, v); apic_write(APIC_LVTT, v);
apic_write(APIC_TMICT, 0xffffffff); apic_write(APIC_TMICT, 0);
break; break;
case CLOCK_EVT_MODE_RESUME: case CLOCK_EVT_MODE_RESUME:
/* Nothing to do here */ /* Nothing to do here */
...@@ -1392,14 +1377,11 @@ void __init enable_IR_x2apic(void) ...@@ -1392,14 +1377,11 @@ void __init enable_IR_x2apic(void)
unsigned long flags; unsigned long flags;
struct IO_APIC_route_entry **ioapic_entries = NULL; struct IO_APIC_route_entry **ioapic_entries = NULL;
int ret, x2apic_enabled = 0; int ret, x2apic_enabled = 0;
int dmar_table_init_ret = 0; int dmar_table_init_ret;
#ifdef CONFIG_INTR_REMAP
dmar_table_init_ret = dmar_table_init(); dmar_table_init_ret = dmar_table_init();
if (dmar_table_init_ret) if (dmar_table_init_ret && !x2apic_supported())
pr_debug("dmar_table_init() failed with %d:\n", return;
dmar_table_init_ret);
#endif
ioapic_entries = alloc_ioapic_entries(); ioapic_entries = alloc_ioapic_entries();
if (!ioapic_entries) { if (!ioapic_entries) {
......
/*
* NOOP APIC driver.
*
* Does almost nothing and should be substituted by a real apic driver via
* probe routine.
*
* Though in case if apic is disabled (for some reason) we try
* to not uglify the caller's code and allow to call (some) apic routines
* like self-ipi, etc...
*/
#include <linux/threads.h>
#include <linux/cpumask.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <asm/fixmap.h>
#include <asm/mpspec.h>
#include <asm/apicdef.h>
#include <asm/apic.h>
#include <asm/setup.h>
#include <linux/smp.h>
#include <asm/ipi.h>
#include <linux/interrupt.h>
#include <asm/acpi.h>
#include <asm/e820.h>
static void noop_init_apic_ldr(void) { }
static void noop_send_IPI_mask(const struct cpumask *cpumask, int vector) { }
static void noop_send_IPI_mask_allbutself(const struct cpumask *cpumask, int vector) { }
static void noop_send_IPI_allbutself(int vector) { }
static void noop_send_IPI_all(int vector) { }
static void noop_send_IPI_self(int vector) { }
static void noop_apic_wait_icr_idle(void) { }
static void noop_apic_icr_write(u32 low, u32 id) { }
static int noop_wakeup_secondary_cpu(int apicid, unsigned long start_eip)
{
return -1;
}
static u32 noop_safe_apic_wait_icr_idle(void)
{
return 0;
}
static u64 noop_apic_icr_read(void)
{
return 0;
}
static int noop_cpu_to_logical_apicid(int cpu)
{
return 0;
}
static int noop_phys_pkg_id(int cpuid_apic, int index_msb)
{
return 0;
}
static unsigned int noop_get_apic_id(unsigned long x)
{
return 0;
}
static int noop_probe(void)
{
/*
* NOOP apic should not ever be
* enabled via probe routine
*/
return 0;
}
static int noop_apic_id_registered(void)
{
/*
* if we would be really "pedantic"
* we should pass read_apic_id() here
* but since NOOP suppose APIC ID = 0
* lets save a few cycles
*/
return physid_isset(0, phys_cpu_present_map);
}
static const struct cpumask *noop_target_cpus(void)
{
/* only BSP here */
return cpumask_of(0);
}
static unsigned long noop_check_apicid_used(physid_mask_t *map, int apicid)
{
return physid_isset(apicid, *map);
}
static unsigned long noop_check_apicid_present(int bit)
{
return physid_isset(bit, phys_cpu_present_map);
}
static void noop_vector_allocation_domain(int cpu, struct cpumask *retmask)
{
if (cpu != 0)
pr_warning("APIC: Vector allocated for non-BSP cpu\n");
cpumask_clear(retmask);
cpumask_set_cpu(cpu, retmask);
}
int noop_apicid_to_node(int logical_apicid)
{
/* we're always on node 0 */
return 0;
}
static u32 noop_apic_read(u32 reg)
{
WARN_ON_ONCE((cpu_has_apic && !disable_apic));
return 0;
}
static void noop_apic_write(u32 reg, u32 v)
{
WARN_ON_ONCE((cpu_has_apic || !disable_apic));
}
struct apic apic_noop = {
.name = "noop",
.probe = noop_probe,
.acpi_madt_oem_check = NULL,
.apic_id_registered = noop_apic_id_registered,
.irq_delivery_mode = dest_LowestPrio,
/* logical delivery broadcast to all CPUs: */
.irq_dest_mode = 1,
.target_cpus = noop_target_cpus,
.disable_esr = 0,
.dest_logical = APIC_DEST_LOGICAL,
.check_apicid_used = noop_check_apicid_used,
.check_apicid_present = noop_check_apicid_present,
.vector_allocation_domain = noop_vector_allocation_domain,
.init_apic_ldr = noop_init_apic_ldr,
.ioapic_phys_id_map = default_ioapic_phys_id_map,
.setup_apic_routing = NULL,
.multi_timer_check = NULL,
.apicid_to_node = noop_apicid_to_node,
.cpu_to_logical_apicid = noop_cpu_to_logical_apicid,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.apicid_to_cpu_present = physid_set_mask_of_physid,
.setup_portio_remap = NULL,
.check_phys_apicid_present = default_check_phys_apicid_present,
.enable_apic_mode = NULL,
.phys_pkg_id = noop_phys_pkg_id,
.mps_oem_check = NULL,
.get_apic_id = noop_get_apic_id,
.set_apic_id = NULL,
.apic_id_mask = 0x0F << 24,
.cpu_mask_to_apicid = default_cpu_mask_to_apicid,
.cpu_mask_to_apicid_and = default_cpu_mask_to_apicid_and,
.send_IPI_mask = noop_send_IPI_mask,
.send_IPI_mask_allbutself = noop_send_IPI_mask_allbutself,
.send_IPI_allbutself = noop_send_IPI_allbutself,
.send_IPI_all = noop_send_IPI_all,
.send_IPI_self = noop_send_IPI_self,
.wakeup_secondary_cpu = noop_wakeup_secondary_cpu,
/* should be safe */
.trampoline_phys_low = DEFAULT_TRAMPOLINE_PHYS_LOW,
.trampoline_phys_high = DEFAULT_TRAMPOLINE_PHYS_HIGH,
.wait_for_init_deassert = NULL,
.smp_callin_clear_local_apic = NULL,
.inquire_remote_apic = NULL,
.read = noop_apic_read,
.write = noop_apic_write,
.icr_read = noop_apic_icr_read,
.icr_write = noop_apic_icr_write,
.wait_icr_idle = noop_apic_wait_icr_idle,
.safe_wait_icr_idle = noop_safe_apic_wait_icr_idle,
};
...@@ -35,7 +35,7 @@ static const struct cpumask *bigsmp_target_cpus(void) ...@@ -35,7 +35,7 @@ static const struct cpumask *bigsmp_target_cpus(void)
#endif #endif
} }
static unsigned long bigsmp_check_apicid_used(physid_mask_t bitmap, int apicid) static unsigned long bigsmp_check_apicid_used(physid_mask_t *map, int apicid)
{ {
return 0; return 0;
} }
...@@ -93,11 +93,6 @@ static int bigsmp_cpu_present_to_apicid(int mps_cpu) ...@@ -93,11 +93,6 @@ static int bigsmp_cpu_present_to_apicid(int mps_cpu)
return BAD_APICID; return BAD_APICID;
} }
static physid_mask_t bigsmp_apicid_to_cpu_present(int phys_apicid)
{
return physid_mask_of_physid(phys_apicid);
}
/* Mapping from cpu number to logical apicid */ /* Mapping from cpu number to logical apicid */
static inline int bigsmp_cpu_to_logical_apicid(int cpu) static inline int bigsmp_cpu_to_logical_apicid(int cpu)
{ {
...@@ -106,10 +101,10 @@ static inline int bigsmp_cpu_to_logical_apicid(int cpu) ...@@ -106,10 +101,10 @@ static inline int bigsmp_cpu_to_logical_apicid(int cpu)
return cpu_physical_id(cpu); return cpu_physical_id(cpu);
} }
static physid_mask_t bigsmp_ioapic_phys_id_map(physid_mask_t phys_map) static void bigsmp_ioapic_phys_id_map(physid_mask_t *phys_map, physid_mask_t *retmap)
{ {
/* For clustered we don't have a good way to do this yet - hack */ /* For clustered we don't have a good way to do this yet - hack */
return physids_promote(0xFFL); physids_promote(0xFFL, retmap);
} }
static int bigsmp_check_phys_apicid_present(int phys_apicid) static int bigsmp_check_phys_apicid_present(int phys_apicid)
...@@ -230,7 +225,7 @@ struct apic apic_bigsmp = { ...@@ -230,7 +225,7 @@ struct apic apic_bigsmp = {
.apicid_to_node = bigsmp_apicid_to_node, .apicid_to_node = bigsmp_apicid_to_node,
.cpu_to_logical_apicid = bigsmp_cpu_to_logical_apicid, .cpu_to_logical_apicid = bigsmp_cpu_to_logical_apicid,
.cpu_present_to_apicid = bigsmp_cpu_present_to_apicid, .cpu_present_to_apicid = bigsmp_cpu_present_to_apicid,
.apicid_to_cpu_present = bigsmp_apicid_to_cpu_present, .apicid_to_cpu_present = physid_set_mask_of_physid,
.setup_portio_remap = NULL, .setup_portio_remap = NULL,
.check_phys_apicid_present = bigsmp_check_phys_apicid_present, .check_phys_apicid_present = bigsmp_check_phys_apicid_present,
.enable_apic_mode = NULL, .enable_apic_mode = NULL,
......
...@@ -466,11 +466,11 @@ static const struct cpumask *es7000_target_cpus(void) ...@@ -466,11 +466,11 @@ static const struct cpumask *es7000_target_cpus(void)
return cpumask_of(smp_processor_id()); return cpumask_of(smp_processor_id());
} }
static unsigned long static unsigned long es7000_check_apicid_used(physid_mask_t *map, int apicid)
es7000_check_apicid_used(physid_mask_t bitmap, int apicid)
{ {
return 0; return 0;
} }
static unsigned long es7000_check_apicid_present(int bit) static unsigned long es7000_check_apicid_present(int bit)
{ {
return physid_isset(bit, phys_cpu_present_map); return physid_isset(bit, phys_cpu_present_map);
...@@ -539,14 +539,10 @@ static int es7000_cpu_present_to_apicid(int mps_cpu) ...@@ -539,14 +539,10 @@ static int es7000_cpu_present_to_apicid(int mps_cpu)
static int cpu_id; static int cpu_id;
static physid_mask_t es7000_apicid_to_cpu_present(int phys_apicid) static void es7000_apicid_to_cpu_present(int phys_apicid, physid_mask_t *retmap)
{ {
physid_mask_t mask; physid_set_mask_of_physid(cpu_id, retmap);
mask = physid_mask_of_physid(cpu_id);
++cpu_id; ++cpu_id;
return mask;
} }
/* Mapping from cpu number to logical apicid */ /* Mapping from cpu number to logical apicid */
...@@ -561,10 +557,10 @@ static int es7000_cpu_to_logical_apicid(int cpu) ...@@ -561,10 +557,10 @@ static int es7000_cpu_to_logical_apicid(int cpu)
#endif #endif
} }
static physid_mask_t es7000_ioapic_phys_id_map(physid_mask_t phys_map) static void es7000_ioapic_phys_id_map(physid_mask_t *phys_map, physid_mask_t *retmap)
{ {
/* For clustered we don't have a good way to do this yet - hack */ /* For clustered we don't have a good way to do this yet - hack */
return physids_promote(0xff); physids_promote(0xFFL, retmap);
} }
static int es7000_check_phys_apicid_present(int cpu_physical_apicid) static int es7000_check_phys_apicid_present(int cpu_physical_apicid)
......
...@@ -60,8 +60,6 @@ ...@@ -60,8 +60,6 @@
#include <asm/irq_remapping.h> #include <asm/irq_remapping.h>
#include <asm/hpet.h> #include <asm/hpet.h>
#include <asm/hw_irq.h> #include <asm/hw_irq.h>
#include <asm/uv/uv_hub.h>
#include <asm/uv/uv_irq.h>
#include <asm/apic.h> #include <asm/apic.h>
...@@ -140,20 +138,6 @@ static struct irq_pin_list *get_one_free_irq_2_pin(int node) ...@@ -140,20 +138,6 @@ static struct irq_pin_list *get_one_free_irq_2_pin(int node)
return pin; return pin;
} }
/*
* This is performance-critical, we want to do it O(1)
*
* Most irqs are mapped 1:1 with pins.
*/
struct irq_cfg {
struct irq_pin_list *irq_2_pin;
cpumask_var_t domain;
cpumask_var_t old_domain;
unsigned move_cleanup_count;
u8 vector;
u8 move_in_progress : 1;
};
/* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */ /* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */
#ifdef CONFIG_SPARSE_IRQ #ifdef CONFIG_SPARSE_IRQ
static struct irq_cfg irq_cfgx[] = { static struct irq_cfg irq_cfgx[] = {
...@@ -209,7 +193,7 @@ int __init arch_early_irq_init(void) ...@@ -209,7 +193,7 @@ int __init arch_early_irq_init(void)
} }
#ifdef CONFIG_SPARSE_IRQ #ifdef CONFIG_SPARSE_IRQ
static struct irq_cfg *irq_cfg(unsigned int irq) struct irq_cfg *irq_cfg(unsigned int irq)
{ {
struct irq_cfg *cfg = NULL; struct irq_cfg *cfg = NULL;
struct irq_desc *desc; struct irq_desc *desc;
...@@ -361,7 +345,7 @@ void arch_free_chip_data(struct irq_desc *old_desc, struct irq_desc *desc) ...@@ -361,7 +345,7 @@ void arch_free_chip_data(struct irq_desc *old_desc, struct irq_desc *desc)
/* end for move_irq_desc */ /* end for move_irq_desc */
#else #else
static struct irq_cfg *irq_cfg(unsigned int irq) struct irq_cfg *irq_cfg(unsigned int irq)
{ {
return irq < nr_irqs ? irq_cfgx + irq : NULL; return irq < nr_irqs ? irq_cfgx + irq : NULL;
} }
...@@ -555,15 +539,12 @@ static void __init replace_pin_at_irq_node(struct irq_cfg *cfg, int node, ...@@ -555,15 +539,12 @@ static void __init replace_pin_at_irq_node(struct irq_cfg *cfg, int node,
add_pin_to_irq_node(cfg, node, newapic, newpin); add_pin_to_irq_node(cfg, node, newapic, newpin);
} }
static void io_apic_modify_irq(struct irq_cfg *cfg, static void __io_apic_modify_irq(struct irq_pin_list *entry,
int mask_and, int mask_or, int mask_and, int mask_or,
void (*final)(struct irq_pin_list *entry)) void (*final)(struct irq_pin_list *entry))
{ {
int pin; unsigned int reg, pin;
struct irq_pin_list *entry;
for_each_irq_pin(entry, cfg->irq_2_pin) {
unsigned int reg;
pin = entry->pin; pin = entry->pin;
reg = io_apic_read(entry->apic, 0x10 + pin * 2); reg = io_apic_read(entry->apic, 0x10 + pin * 2);
reg &= mask_and; reg &= mask_and;
...@@ -571,7 +552,28 @@ static void io_apic_modify_irq(struct irq_cfg *cfg, ...@@ -571,7 +552,28 @@ static void io_apic_modify_irq(struct irq_cfg *cfg,
io_apic_modify(entry->apic, 0x10 + pin * 2, reg); io_apic_modify(entry->apic, 0x10 + pin * 2, reg);
if (final) if (final)
final(entry); final(entry);
} }
static void io_apic_modify_irq(struct irq_cfg *cfg,
int mask_and, int mask_or,
void (*final)(struct irq_pin_list *entry))
{
struct irq_pin_list *entry;
for_each_irq_pin(entry, cfg->irq_2_pin)
__io_apic_modify_irq(entry, mask_and, mask_or, final);
}
static void __mask_and_edge_IO_APIC_irq(struct irq_pin_list *entry)
{
__io_apic_modify_irq(entry, ~IO_APIC_REDIR_LEVEL_TRIGGER,
IO_APIC_REDIR_MASKED, NULL);
}
static void __unmask_and_level_IO_APIC_irq(struct irq_pin_list *entry)
{
__io_apic_modify_irq(entry, ~IO_APIC_REDIR_MASKED,
IO_APIC_REDIR_LEVEL_TRIGGER, NULL);
} }
static void __unmask_IO_APIC_irq(struct irq_cfg *cfg) static void __unmask_IO_APIC_irq(struct irq_cfg *cfg)
...@@ -595,18 +597,6 @@ static void __mask_IO_APIC_irq(struct irq_cfg *cfg) ...@@ -595,18 +597,6 @@ static void __mask_IO_APIC_irq(struct irq_cfg *cfg)
io_apic_modify_irq(cfg, ~0, IO_APIC_REDIR_MASKED, &io_apic_sync); io_apic_modify_irq(cfg, ~0, IO_APIC_REDIR_MASKED, &io_apic_sync);
} }
static void __mask_and_edge_IO_APIC_irq(struct irq_cfg *cfg)
{
io_apic_modify_irq(cfg, ~IO_APIC_REDIR_LEVEL_TRIGGER,
IO_APIC_REDIR_MASKED, NULL);
}
static void __unmask_and_level_IO_APIC_irq(struct irq_cfg *cfg)
{
io_apic_modify_irq(cfg, ~IO_APIC_REDIR_MASKED,
IO_APIC_REDIR_LEVEL_TRIGGER, NULL);
}
static void mask_IO_APIC_irq_desc(struct irq_desc *desc) static void mask_IO_APIC_irq_desc(struct irq_desc *desc)
{ {
struct irq_cfg *cfg = desc->chip_data; struct irq_cfg *cfg = desc->chip_data;
...@@ -1177,7 +1167,7 @@ __assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask) ...@@ -1177,7 +1167,7 @@ __assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
int cpu, err; int cpu, err;
cpumask_var_t tmp_mask; cpumask_var_t tmp_mask;
if ((cfg->move_in_progress) || cfg->move_cleanup_count) if (cfg->move_in_progress)
return -EBUSY; return -EBUSY;
if (!alloc_cpumask_var(&tmp_mask, GFP_ATOMIC)) if (!alloc_cpumask_var(&tmp_mask, GFP_ATOMIC))
...@@ -1237,8 +1227,7 @@ __assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask) ...@@ -1237,8 +1227,7 @@ __assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
return err; return err;
} }
static int int assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
{ {
int err; int err;
unsigned long flags; unsigned long flags;
...@@ -1599,9 +1588,6 @@ __apicdebuginit(void) print_IO_APIC(void) ...@@ -1599,9 +1588,6 @@ __apicdebuginit(void) print_IO_APIC(void)
struct irq_desc *desc; struct irq_desc *desc;
unsigned int irq; unsigned int irq;
if (apic_verbosity == APIC_QUIET)
return;
printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries); printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries);
for (i = 0; i < nr_ioapics; i++) for (i = 0; i < nr_ioapics; i++)
printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n", printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n",
...@@ -1708,9 +1694,6 @@ __apicdebuginit(void) print_APIC_field(int base) ...@@ -1708,9 +1694,6 @@ __apicdebuginit(void) print_APIC_field(int base)
{ {
int i; int i;
if (apic_verbosity == APIC_QUIET)
return;
printk(KERN_DEBUG); printk(KERN_DEBUG);
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
...@@ -1724,9 +1707,6 @@ __apicdebuginit(void) print_local_APIC(void *dummy) ...@@ -1724,9 +1707,6 @@ __apicdebuginit(void) print_local_APIC(void *dummy)
unsigned int i, v, ver, maxlvt; unsigned int i, v, ver, maxlvt;
u64 icr; u64 icr;
if (apic_verbosity == APIC_QUIET)
return;
printk(KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n", printk(KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n",
smp_processor_id(), hard_smp_processor_id()); smp_processor_id(), hard_smp_processor_id());
v = apic_read(APIC_ID); v = apic_read(APIC_ID);
...@@ -1824,13 +1804,19 @@ __apicdebuginit(void) print_local_APIC(void *dummy) ...@@ -1824,13 +1804,19 @@ __apicdebuginit(void) print_local_APIC(void *dummy)
printk("\n"); printk("\n");
} }
__apicdebuginit(void) print_all_local_APICs(void) __apicdebuginit(void) print_local_APICs(int maxcpu)
{ {
int cpu; int cpu;
if (!maxcpu)
return;
preempt_disable(); preempt_disable();
for_each_online_cpu(cpu) for_each_online_cpu(cpu) {
if (cpu >= maxcpu)
break;
smp_call_function_single(cpu, print_local_APIC, NULL, 1); smp_call_function_single(cpu, print_local_APIC, NULL, 1);
}
preempt_enable(); preempt_enable();
} }
...@@ -1839,7 +1825,7 @@ __apicdebuginit(void) print_PIC(void) ...@@ -1839,7 +1825,7 @@ __apicdebuginit(void) print_PIC(void)
unsigned int v; unsigned int v;
unsigned long flags; unsigned long flags;
if (apic_verbosity == APIC_QUIET || !nr_legacy_irqs) if (!nr_legacy_irqs)
return; return;
printk(KERN_DEBUG "\nprinting PIC contents\n"); printk(KERN_DEBUG "\nprinting PIC contents\n");
...@@ -1866,21 +1852,41 @@ __apicdebuginit(void) print_PIC(void) ...@@ -1866,21 +1852,41 @@ __apicdebuginit(void) print_PIC(void)
printk(KERN_DEBUG "... PIC ELCR: %04x\n", v); printk(KERN_DEBUG "... PIC ELCR: %04x\n", v);
} }
__apicdebuginit(int) print_all_ICs(void) static int __initdata show_lapic = 1;
static __init int setup_show_lapic(char *arg)
{ {
int num = -1;
if (strcmp(arg, "all") == 0) {
show_lapic = CONFIG_NR_CPUS;
} else {
get_option(&arg, &num);
if (num >= 0)
show_lapic = num;
}
return 1;
}
__setup("show_lapic=", setup_show_lapic);
__apicdebuginit(int) print_ICs(void)
{
if (apic_verbosity == APIC_QUIET)
return 0;
print_PIC(); print_PIC();
/* don't print out if apic is not there */ /* don't print out if apic is not there */
if (!cpu_has_apic && !apic_from_smp_config()) if (!cpu_has_apic && !apic_from_smp_config())
return 0; return 0;
print_all_local_APICs(); print_local_APICs(show_lapic);
print_IO_APIC(); print_IO_APIC();
return 0; return 0;
} }
fs_initcall(print_all_ICs); fs_initcall(print_ICs);
/* Where if anywhere is the i8259 connect in external int mode */ /* Where if anywhere is the i8259 connect in external int mode */
...@@ -2031,7 +2037,7 @@ void __init setup_ioapic_ids_from_mpc(void) ...@@ -2031,7 +2037,7 @@ void __init setup_ioapic_ids_from_mpc(void)
* This is broken; anything with a real cpu count has to * This is broken; anything with a real cpu count has to
* circumvent this idiocy regardless. * circumvent this idiocy regardless.
*/ */
phys_id_present_map = apic->ioapic_phys_id_map(phys_cpu_present_map); apic->ioapic_phys_id_map(&phys_cpu_present_map, &phys_id_present_map);
/* /*
* Set the IOAPIC ID to the value stored in the MPC table. * Set the IOAPIC ID to the value stored in the MPC table.
...@@ -2058,7 +2064,7 @@ void __init setup_ioapic_ids_from_mpc(void) ...@@ -2058,7 +2064,7 @@ void __init setup_ioapic_ids_from_mpc(void)
* system must have a unique ID or we get lots of nice * system must have a unique ID or we get lots of nice
* 'stuck on smp_invalidate_needed IPI wait' messages. * 'stuck on smp_invalidate_needed IPI wait' messages.
*/ */
if (apic->check_apicid_used(phys_id_present_map, if (apic->check_apicid_used(&phys_id_present_map,
mp_ioapics[apic_id].apicid)) { mp_ioapics[apic_id].apicid)) {
printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n", printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n",
apic_id, mp_ioapics[apic_id].apicid); apic_id, mp_ioapics[apic_id].apicid);
...@@ -2073,7 +2079,7 @@ void __init setup_ioapic_ids_from_mpc(void) ...@@ -2073,7 +2079,7 @@ void __init setup_ioapic_ids_from_mpc(void)
mp_ioapics[apic_id].apicid = i; mp_ioapics[apic_id].apicid = i;
} else { } else {
physid_mask_t tmp; physid_mask_t tmp;
tmp = apic->apicid_to_cpu_present(mp_ioapics[apic_id].apicid); apic->apicid_to_cpu_present(mp_ioapics[apic_id].apicid, &tmp);
apic_printk(APIC_VERBOSE, "Setting %d in the " apic_printk(APIC_VERBOSE, "Setting %d in the "
"phys_id_present_map\n", "phys_id_present_map\n",
mp_ioapics[apic_id].apicid); mp_ioapics[apic_id].apicid);
...@@ -2228,20 +2234,16 @@ static int ioapic_retrigger_irq(unsigned int irq) ...@@ -2228,20 +2234,16 @@ static int ioapic_retrigger_irq(unsigned int irq)
*/ */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static void send_cleanup_vector(struct irq_cfg *cfg) void send_cleanup_vector(struct irq_cfg *cfg)
{ {
cpumask_var_t cleanup_mask; cpumask_var_t cleanup_mask;
if (unlikely(!alloc_cpumask_var(&cleanup_mask, GFP_ATOMIC))) { if (unlikely(!alloc_cpumask_var(&cleanup_mask, GFP_ATOMIC))) {
unsigned int i; unsigned int i;
cfg->move_cleanup_count = 0;
for_each_cpu_and(i, cfg->old_domain, cpu_online_mask)
cfg->move_cleanup_count++;
for_each_cpu_and(i, cfg->old_domain, cpu_online_mask) for_each_cpu_and(i, cfg->old_domain, cpu_online_mask)
apic->send_IPI_mask(cpumask_of(i), IRQ_MOVE_CLEANUP_VECTOR); apic->send_IPI_mask(cpumask_of(i), IRQ_MOVE_CLEANUP_VECTOR);
} else { } else {
cpumask_and(cleanup_mask, cfg->old_domain, cpu_online_mask); cpumask_and(cleanup_mask, cfg->old_domain, cpu_online_mask);
cfg->move_cleanup_count = cpumask_weight(cleanup_mask);
apic->send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR); apic->send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR);
free_cpumask_var(cleanup_mask); free_cpumask_var(cleanup_mask);
} }
...@@ -2272,15 +2274,12 @@ static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, struct irq ...@@ -2272,15 +2274,12 @@ static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, struct irq
} }
} }
static int
assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask);
/* /*
* Either sets desc->affinity to a valid value, and returns * Either sets desc->affinity to a valid value, and returns
* ->cpu_mask_to_apicid of that, or returns BAD_APICID and * ->cpu_mask_to_apicid of that, or returns BAD_APICID and
* leaves desc->affinity untouched. * leaves desc->affinity untouched.
*/ */
static unsigned int unsigned int
set_desc_affinity(struct irq_desc *desc, const struct cpumask *mask) set_desc_affinity(struct irq_desc *desc, const struct cpumask *mask)
{ {
struct irq_cfg *cfg; struct irq_cfg *cfg;
...@@ -2433,8 +2432,6 @@ asmlinkage void smp_irq_move_cleanup_interrupt(void) ...@@ -2433,8 +2432,6 @@ asmlinkage void smp_irq_move_cleanup_interrupt(void)
cfg = irq_cfg(irq); cfg = irq_cfg(irq);
spin_lock(&desc->lock); spin_lock(&desc->lock);
if (!cfg->move_cleanup_count)
goto unlock;
if (vector == cfg->vector && cpumask_test_cpu(me, cfg->domain)) if (vector == cfg->vector && cpumask_test_cpu(me, cfg->domain))
goto unlock; goto unlock;
...@@ -2452,7 +2449,6 @@ asmlinkage void smp_irq_move_cleanup_interrupt(void) ...@@ -2452,7 +2449,6 @@ asmlinkage void smp_irq_move_cleanup_interrupt(void)
goto unlock; goto unlock;
} }
__get_cpu_var(vector_irq)[vector] = -1; __get_cpu_var(vector_irq)[vector] = -1;
cfg->move_cleanup_count--;
unlock: unlock:
spin_unlock(&desc->lock); spin_unlock(&desc->lock);
} }
...@@ -2460,21 +2456,33 @@ asmlinkage void smp_irq_move_cleanup_interrupt(void) ...@@ -2460,21 +2456,33 @@ asmlinkage void smp_irq_move_cleanup_interrupt(void)
irq_exit(); irq_exit();
} }
static void irq_complete_move(struct irq_desc **descp) static void __irq_complete_move(struct irq_desc **descp, unsigned vector)
{ {
struct irq_desc *desc = *descp; struct irq_desc *desc = *descp;
struct irq_cfg *cfg = desc->chip_data; struct irq_cfg *cfg = desc->chip_data;
unsigned vector, me; unsigned me;
if (likely(!cfg->move_in_progress)) if (likely(!cfg->move_in_progress))
return; return;
vector = ~get_irq_regs()->orig_ax;
me = smp_processor_id(); me = smp_processor_id();
if (vector == cfg->vector && cpumask_test_cpu(me, cfg->domain)) if (vector == cfg->vector && cpumask_test_cpu(me, cfg->domain))
send_cleanup_vector(cfg); send_cleanup_vector(cfg);
} }
static void irq_complete_move(struct irq_desc **descp)
{
__irq_complete_move(descp, ~get_irq_regs()->orig_ax);
}
void irq_force_complete_move(int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irq_cfg *cfg = desc->chip_data;
__irq_complete_move(&desc, cfg->vector);
}
#else #else
static inline void irq_complete_move(struct irq_desc **descp) {} static inline void irq_complete_move(struct irq_desc **descp) {}
#endif #endif
...@@ -2490,6 +2498,59 @@ static void ack_apic_edge(unsigned int irq) ...@@ -2490,6 +2498,59 @@ static void ack_apic_edge(unsigned int irq)
atomic_t irq_mis_count; atomic_t irq_mis_count;
/*
* IO-APIC versions below 0x20 don't support EOI register.
* For the record, here is the information about various versions:
* 0Xh 82489DX
* 1Xh I/OAPIC or I/O(x)APIC which are not PCI 2.2 Compliant
* 2Xh I/O(x)APIC which is PCI 2.2 Compliant
* 30h-FFh Reserved
*
* Some of the Intel ICH Specs (ICH2 to ICH5) documents the io-apic
* version as 0x2. This is an error with documentation and these ICH chips
* use io-apic's of version 0x20.
*
* For IO-APIC's with EOI register, we use that to do an explicit EOI.
* Otherwise, we simulate the EOI message manually by changing the trigger
* mode to edge and then back to level, with RTE being masked during this.
*/
static void __eoi_ioapic_irq(unsigned int irq, struct irq_cfg *cfg)
{
struct irq_pin_list *entry;
for_each_irq_pin(entry, cfg->irq_2_pin) {
if (mp_ioapics[entry->apic].apicver >= 0x20) {
/*
* Intr-remapping uses pin number as the virtual vector
* in the RTE. Actual vector is programmed in
* intr-remapping table entry. Hence for the io-apic
* EOI we use the pin number.
*/
if (irq_remapped(irq))
io_apic_eoi(entry->apic, entry->pin);
else
io_apic_eoi(entry->apic, cfg->vector);
} else {
__mask_and_edge_IO_APIC_irq(entry);
__unmask_and_level_IO_APIC_irq(entry);
}
}
}
static void eoi_ioapic_irq(struct irq_desc *desc)
{
struct irq_cfg *cfg;
unsigned long flags;
unsigned int irq;
irq = desc->irq;
cfg = desc->chip_data;
spin_lock_irqsave(&ioapic_lock, flags);
__eoi_ioapic_irq(irq, cfg);
spin_unlock_irqrestore(&ioapic_lock, flags);
}
static void ack_apic_level(unsigned int irq) static void ack_apic_level(unsigned int irq)
{ {
struct irq_desc *desc = irq_to_desc(irq); struct irq_desc *desc = irq_to_desc(irq);
...@@ -2525,6 +2586,19 @@ static void ack_apic_level(unsigned int irq) ...@@ -2525,6 +2586,19 @@ static void ack_apic_level(unsigned int irq)
* level-triggered interrupt. We mask the source for the time of the * level-triggered interrupt. We mask the source for the time of the
* operation to prevent an edge-triggered interrupt escaping meanwhile. * operation to prevent an edge-triggered interrupt escaping meanwhile.
* The idea is from Manfred Spraul. --macro * The idea is from Manfred Spraul. --macro
*
* Also in the case when cpu goes offline, fixup_irqs() will forward
* any unhandled interrupt on the offlined cpu to the new cpu
* destination that is handling the corresponding interrupt. This
* interrupt forwarding is done via IPI's. Hence, in this case also
* level-triggered io-apic interrupt will be seen as an edge
* interrupt in the IRR. And we can't rely on the cpu's EOI
* to be broadcasted to the IO-APIC's which will clear the remoteIRR
* corresponding to the level-triggered interrupt. Hence on IO-APIC's
* supporting EOI register, we do an explicit EOI to clear the
* remote IRR and on IO-APIC's which don't have an EOI register,
* we use the above logic (mask+edge followed by unmask+level) from
* Manfred Spraul to clear the remote IRR.
*/ */
cfg = desc->chip_data; cfg = desc->chip_data;
i = cfg->vector; i = cfg->vector;
...@@ -2536,6 +2610,19 @@ static void ack_apic_level(unsigned int irq) ...@@ -2536,6 +2610,19 @@ static void ack_apic_level(unsigned int irq)
*/ */
ack_APIC_irq(); ack_APIC_irq();
/*
* Tail end of clearing remote IRR bit (either by delivering the EOI
* message via io-apic EOI register write or simulating it using
* mask+edge followed by unnask+level logic) manually when the
* level triggered interrupt is seen as the edge triggered interrupt
* at the cpu.
*/
if (!(v & (1 << (i & 0x1f)))) {
atomic_inc(&irq_mis_count);
eoi_ioapic_irq(desc);
}
/* Now we can move and renable the irq */ /* Now we can move and renable the irq */
if (unlikely(do_unmask_irq)) { if (unlikely(do_unmask_irq)) {
/* Only migrate the irq if the ack has been received. /* Only migrate the irq if the ack has been received.
...@@ -2569,41 +2656,9 @@ static void ack_apic_level(unsigned int irq) ...@@ -2569,41 +2656,9 @@ static void ack_apic_level(unsigned int irq)
move_masked_irq(irq); move_masked_irq(irq);
unmask_IO_APIC_irq_desc(desc); unmask_IO_APIC_irq_desc(desc);
} }
/* Tail end of version 0x11 I/O APIC bug workaround */
if (!(v & (1 << (i & 0x1f)))) {
atomic_inc(&irq_mis_count);
spin_lock(&ioapic_lock);
__mask_and_edge_IO_APIC_irq(cfg);
__unmask_and_level_IO_APIC_irq(cfg);
spin_unlock(&ioapic_lock);
}
} }
#ifdef CONFIG_INTR_REMAP #ifdef CONFIG_INTR_REMAP
static void __eoi_ioapic_irq(unsigned int irq, struct irq_cfg *cfg)
{
struct irq_pin_list *entry;
for_each_irq_pin(entry, cfg->irq_2_pin)
io_apic_eoi(entry->apic, entry->pin);
}
static void
eoi_ioapic_irq(struct irq_desc *desc)
{
struct irq_cfg *cfg;
unsigned long flags;
unsigned int irq;
irq = desc->irq;
cfg = desc->chip_data;
spin_lock_irqsave(&ioapic_lock, flags);
__eoi_ioapic_irq(irq, cfg);
spin_unlock_irqrestore(&ioapic_lock, flags);
}
static void ir_ack_apic_edge(unsigned int irq) static void ir_ack_apic_edge(unsigned int irq)
{ {
ack_APIC_irq(); ack_APIC_irq();
...@@ -3157,6 +3212,7 @@ unsigned int create_irq_nr(unsigned int irq_want, int node) ...@@ -3157,6 +3212,7 @@ unsigned int create_irq_nr(unsigned int irq_want, int node)
continue; continue;
desc_new = move_irq_desc(desc_new, node); desc_new = move_irq_desc(desc_new, node);
cfg_new = desc_new->chip_data;
if (__assign_irq_vector(new, cfg_new, apic->target_cpus()) == 0) if (__assign_irq_vector(new, cfg_new, apic->target_cpus()) == 0)
irq = new; irq = new;
...@@ -3708,75 +3764,6 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) ...@@ -3708,75 +3764,6 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
} }
#endif /* CONFIG_HT_IRQ */ #endif /* CONFIG_HT_IRQ */
#ifdef CONFIG_X86_UV
/*
* Re-target the irq to the specified CPU and enable the specified MMR located
* on the specified blade to allow the sending of MSIs to the specified CPU.
*/
int arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade,
unsigned long mmr_offset)
{
const struct cpumask *eligible_cpu = cpumask_of(cpu);
struct irq_cfg *cfg;
int mmr_pnode;
unsigned long mmr_value;
struct uv_IO_APIC_route_entry *entry;
unsigned long flags;
int err;
BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) != sizeof(unsigned long));
cfg = irq_cfg(irq);
err = assign_irq_vector(irq, cfg, eligible_cpu);
if (err != 0)
return err;
spin_lock_irqsave(&vector_lock, flags);
set_irq_chip_and_handler_name(irq, &uv_irq_chip, handle_percpu_irq,
irq_name);
spin_unlock_irqrestore(&vector_lock, flags);
mmr_value = 0;
entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
entry->vector = cfg->vector;
entry->delivery_mode = apic->irq_delivery_mode;
entry->dest_mode = apic->irq_dest_mode;
entry->polarity = 0;
entry->trigger = 0;
entry->mask = 0;
entry->dest = apic->cpu_mask_to_apicid(eligible_cpu);
mmr_pnode = uv_blade_to_pnode(mmr_blade);
uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
if (cfg->move_in_progress)
send_cleanup_vector(cfg);
return irq;
}
/*
* Disable the specified MMR located on the specified blade so that MSIs are
* longer allowed to be sent.
*/
void arch_disable_uv_irq(int mmr_blade, unsigned long mmr_offset)
{
unsigned long mmr_value;
struct uv_IO_APIC_route_entry *entry;
int mmr_pnode;
BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) != sizeof(unsigned long));
mmr_value = 0;
entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
entry->mask = 1;
mmr_pnode = uv_blade_to_pnode(mmr_blade);
uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
}
#endif /* CONFIG_X86_64 */
int __init io_apic_get_redir_entries (int ioapic) int __init io_apic_get_redir_entries (int ioapic)
{ {
union IO_APIC_reg_01 reg_01; union IO_APIC_reg_01 reg_01;
...@@ -3944,7 +3931,7 @@ int __init io_apic_get_unique_id(int ioapic, int apic_id) ...@@ -3944,7 +3931,7 @@ int __init io_apic_get_unique_id(int ioapic, int apic_id)
*/ */
if (physids_empty(apic_id_map)) if (physids_empty(apic_id_map))
apic_id_map = apic->ioapic_phys_id_map(phys_cpu_present_map); apic->ioapic_phys_id_map(&phys_cpu_present_map, &apic_id_map);
spin_lock_irqsave(&ioapic_lock, flags); spin_lock_irqsave(&ioapic_lock, flags);
reg_00.raw = io_apic_read(ioapic, 0); reg_00.raw = io_apic_read(ioapic, 0);
...@@ -3960,10 +3947,10 @@ int __init io_apic_get_unique_id(int ioapic, int apic_id) ...@@ -3960,10 +3947,10 @@ int __init io_apic_get_unique_id(int ioapic, int apic_id)
* Every APIC in a system must have a unique ID or we get lots of nice * Every APIC in a system must have a unique ID or we get lots of nice
* 'stuck on smp_invalidate_needed IPI wait' messages. * 'stuck on smp_invalidate_needed IPI wait' messages.
*/ */
if (apic->check_apicid_used(apic_id_map, apic_id)) { if (apic->check_apicid_used(&apic_id_map, apic_id)) {
for (i = 0; i < get_physical_broadcast(); i++) { for (i = 0; i < get_physical_broadcast(); i++) {
if (!apic->check_apicid_used(apic_id_map, i)) if (!apic->check_apicid_used(&apic_id_map, i))
break; break;
} }
...@@ -3976,7 +3963,7 @@ int __init io_apic_get_unique_id(int ioapic, int apic_id) ...@@ -3976,7 +3963,7 @@ int __init io_apic_get_unique_id(int ioapic, int apic_id)
apic_id = i; apic_id = i;
} }
tmp = apic->apicid_to_cpu_present(apic_id); apic->apicid_to_cpu_present(apic_id, &tmp);
physids_or(apic_id_map, apic_id_map, tmp); physids_or(apic_id_map, apic_id_map, tmp);
if (reg_00.bits.ID != apic_id) { if (reg_00.bits.ID != apic_id) {
...@@ -4106,7 +4093,7 @@ static struct resource * __init ioapic_setup_resources(int nr_ioapics) ...@@ -4106,7 +4093,7 @@ static struct resource * __init ioapic_setup_resources(int nr_ioapics)
for (i = 0; i < nr_ioapics; i++) { for (i = 0; i < nr_ioapics; i++) {
res[i].name = mem; res[i].name = mem;
res[i].flags = IORESOURCE_MEM | IORESOURCE_BUSY; res[i].flags = IORESOURCE_MEM | IORESOURCE_BUSY;
sprintf(mem, "IOAPIC %u", i); snprintf(mem, IOAPIC_RESOURCE_NAME_SIZE, "IOAPIC %u", i);
mem += IOAPIC_RESOURCE_NAME_SIZE; mem += IOAPIC_RESOURCE_NAME_SIZE;
} }
...@@ -4140,18 +4127,17 @@ void __init ioapic_init_mappings(void) ...@@ -4140,18 +4127,17 @@ void __init ioapic_init_mappings(void)
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
fake_ioapic_page: fake_ioapic_page:
#endif #endif
ioapic_phys = (unsigned long) ioapic_phys = (unsigned long)alloc_bootmem_pages(PAGE_SIZE);
alloc_bootmem_pages(PAGE_SIZE);
ioapic_phys = __pa(ioapic_phys); ioapic_phys = __pa(ioapic_phys);
} }
set_fixmap_nocache(idx, ioapic_phys); set_fixmap_nocache(idx, ioapic_phys);
apic_printk(APIC_VERBOSE, apic_printk(APIC_VERBOSE, "mapped IOAPIC to %08lx (%08lx)\n",
"mapped IOAPIC to %08lx (%08lx)\n", __fix_to_virt(idx) + (ioapic_phys & ~PAGE_MASK),
__fix_to_virt(idx), ioapic_phys); ioapic_phys);
idx++; idx++;
ioapic_res->start = ioapic_phys; ioapic_res->start = ioapic_phys;
ioapic_res->end = ioapic_phys + (4 * 1024) - 1; ioapic_res->end = ioapic_phys + IO_APIC_SLOT_SIZE - 1;
ioapic_res++; ioapic_res++;
} }
} }
......
...@@ -334,10 +334,9 @@ static inline const struct cpumask *numaq_target_cpus(void) ...@@ -334,10 +334,9 @@ static inline const struct cpumask *numaq_target_cpus(void)
return cpu_all_mask; return cpu_all_mask;
} }
static inline unsigned long static unsigned long numaq_check_apicid_used(physid_mask_t *map, int apicid)
numaq_check_apicid_used(physid_mask_t bitmap, int apicid)
{ {
return physid_isset(apicid, bitmap); return physid_isset(apicid, *map);
} }
static inline unsigned long numaq_check_apicid_present(int bit) static inline unsigned long numaq_check_apicid_present(int bit)
...@@ -371,10 +370,10 @@ static inline int numaq_multi_timer_check(int apic, int irq) ...@@ -371,10 +370,10 @@ static inline int numaq_multi_timer_check(int apic, int irq)
return apic != 0 && irq == 0; return apic != 0 && irq == 0;
} }
static inline physid_mask_t numaq_ioapic_phys_id_map(physid_mask_t phys_map) static inline void numaq_ioapic_phys_id_map(physid_mask_t *phys_map, physid_mask_t *retmap)
{ {
/* We don't have a good way to do this yet - hack */ /* We don't have a good way to do this yet - hack */
return physids_promote(0xFUL); return physids_promote(0xFUL, retmap);
} }
static inline int numaq_cpu_to_logical_apicid(int cpu) static inline int numaq_cpu_to_logical_apicid(int cpu)
...@@ -402,12 +401,12 @@ static inline int numaq_apicid_to_node(int logical_apicid) ...@@ -402,12 +401,12 @@ static inline int numaq_apicid_to_node(int logical_apicid)
return logical_apicid >> 4; return logical_apicid >> 4;
} }
static inline physid_mask_t numaq_apicid_to_cpu_present(int logical_apicid) static void numaq_apicid_to_cpu_present(int logical_apicid, physid_mask_t *retmap)
{ {
int node = numaq_apicid_to_node(logical_apicid); int node = numaq_apicid_to_node(logical_apicid);
int cpu = __ffs(logical_apicid & 0xf); int cpu = __ffs(logical_apicid & 0xf);
return physid_mask_of_physid(cpu + 4*node); physid_set_mask_of_physid(cpu + 4*node, retmap);
} }
/* Where the IO area was mapped on multiquad, always 0 otherwise */ /* Where the IO area was mapped on multiquad, always 0 otherwise */
......
...@@ -108,7 +108,7 @@ struct apic apic_default = { ...@@ -108,7 +108,7 @@ struct apic apic_default = {
.apicid_to_node = default_apicid_to_node, .apicid_to_node = default_apicid_to_node,
.cpu_to_logical_apicid = default_cpu_to_logical_apicid, .cpu_to_logical_apicid = default_cpu_to_logical_apicid,
.cpu_present_to_apicid = default_cpu_present_to_apicid, .cpu_present_to_apicid = default_cpu_present_to_apicid,
.apicid_to_cpu_present = default_apicid_to_cpu_present, .apicid_to_cpu_present = physid_set_mask_of_physid,
.setup_portio_remap = NULL, .setup_portio_remap = NULL,
.check_phys_apicid_present = default_check_phys_apicid_present, .check_phys_apicid_present = default_check_phys_apicid_present,
.enable_apic_mode = NULL, .enable_apic_mode = NULL,
......
...@@ -183,7 +183,7 @@ static const struct cpumask *summit_target_cpus(void) ...@@ -183,7 +183,7 @@ static const struct cpumask *summit_target_cpus(void)
return cpumask_of(0); return cpumask_of(0);
} }
static unsigned long summit_check_apicid_used(physid_mask_t bitmap, int apicid) static unsigned long summit_check_apicid_used(physid_mask_t *map, int apicid)
{ {
return 0; return 0;
} }
...@@ -261,15 +261,15 @@ static int summit_cpu_present_to_apicid(int mps_cpu) ...@@ -261,15 +261,15 @@ static int summit_cpu_present_to_apicid(int mps_cpu)
return BAD_APICID; return BAD_APICID;
} }
static physid_mask_t summit_ioapic_phys_id_map(physid_mask_t phys_id_map) static void summit_ioapic_phys_id_map(physid_mask_t *phys_id_map, physid_mask_t *retmap)
{ {
/* For clustered we don't have a good way to do this yet - hack */ /* For clustered we don't have a good way to do this yet - hack */
return physids_promote(0x0F); physids_promote(0x0FL, retmap);
} }
static physid_mask_t summit_apicid_to_cpu_present(int apicid) static void summit_apicid_to_cpu_present(int apicid, physid_mask_t *retmap)
{ {
return physid_mask_of_physid(0); physid_set_mask_of_physid(0, retmap);
} }
static int summit_check_phys_apicid_present(int physical_apicid) static int summit_check_phys_apicid_present(int physical_apicid)
......
...@@ -409,6 +409,12 @@ static __init void map_mmioh_high(int max_pnode) ...@@ -409,6 +409,12 @@ static __init void map_mmioh_high(int max_pnode)
map_high("MMIOH", mmioh.s.base, shift, max_pnode, map_uc); map_high("MMIOH", mmioh.s.base, shift, max_pnode, map_uc);
} }
static __init void map_low_mmrs(void)
{
init_extra_mapping_uc(UV_GLOBAL_MMR32_BASE, UV_GLOBAL_MMR32_SIZE);
init_extra_mapping_uc(UV_LOCAL_MMR_BASE, UV_LOCAL_MMR_SIZE);
}
static __init void uv_rtc_init(void) static __init void uv_rtc_init(void)
{ {
long status; long status;
...@@ -550,6 +556,8 @@ void __init uv_system_init(void) ...@@ -550,6 +556,8 @@ void __init uv_system_init(void)
unsigned long mmr_base, present, paddr; unsigned long mmr_base, present, paddr;
unsigned short pnode_mask; unsigned short pnode_mask;
map_low_mmrs();
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; m_val = m_n_config.s.m_skt;
n_val = m_n_config.s.n_skt; n_val = m_n_config.s.n_skt;
......
...@@ -712,7 +712,7 @@ static void probe_nmi_watchdog(void) ...@@ -712,7 +712,7 @@ static void probe_nmi_watchdog(void)
switch (boot_cpu_data.x86_vendor) { switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD: case X86_VENDOR_AMD:
if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15 && if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15 &&
boot_cpu_data.x86 != 16) boot_cpu_data.x86 != 16 && boot_cpu_data.x86 != 17)
return; return;
wd_ops = &k7_wd_ops; wd_ops = &k7_wd_ops;
break; break;
......
...@@ -274,3 +274,93 @@ void smp_generic_interrupt(struct pt_regs *regs) ...@@ -274,3 +274,93 @@ void smp_generic_interrupt(struct pt_regs *regs)
} }
EXPORT_SYMBOL_GPL(vector_used_by_percpu_irq); EXPORT_SYMBOL_GPL(vector_used_by_percpu_irq);
#ifdef CONFIG_HOTPLUG_CPU
/* A cpu has been removed from cpu_online_mask. Reset irq affinities. */
void fixup_irqs(void)
{
unsigned int irq, vector;
static int warned;
struct irq_desc *desc;
for_each_irq_desc(irq, desc) {
int break_affinity = 0;
int set_affinity = 1;
const struct cpumask *affinity;
if (!desc)
continue;
if (irq == 2)
continue;
/* interrupt's are disabled at this point */
spin_lock(&desc->lock);
affinity = desc->affinity;
if (!irq_has_action(irq) ||
cpumask_equal(affinity, cpu_online_mask)) {
spin_unlock(&desc->lock);
continue;
}
/*
* Complete the irq move. This cpu is going down and for
* non intr-remapping case, we can't wait till this interrupt
* arrives at this cpu before completing the irq move.
*/
irq_force_complete_move(irq);
if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
break_affinity = 1;
affinity = cpu_all_mask;
}
if (!(desc->status & IRQ_MOVE_PCNTXT) && desc->chip->mask)
desc->chip->mask(irq);
if (desc->chip->set_affinity)
desc->chip->set_affinity(irq, affinity);
else if (!(warned++))
set_affinity = 0;
if (!(desc->status & IRQ_MOVE_PCNTXT) && desc->chip->unmask)
desc->chip->unmask(irq);
spin_unlock(&desc->lock);
if (break_affinity && set_affinity)
printk("Broke affinity for irq %i\n", irq);
else if (!set_affinity)
printk("Cannot set affinity for irq %i\n", irq);
}
/*
* We can remove mdelay() and then send spuriuous interrupts to
* new cpu targets for all the irqs that were handled previously by
* this cpu. While it works, I have seen spurious interrupt messages
* (nothing wrong but still...).
*
* So for now, retain mdelay(1) and check the IRR and then send those
* interrupts to new targets as this cpu is already offlined...
*/
mdelay(1);
for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS; vector++) {
unsigned int irr;
if (__get_cpu_var(vector_irq)[vector] < 0)
continue;
irr = apic_read(APIC_IRR + (vector / 32 * 0x10));
if (irr & (1 << (vector % 32))) {
irq = __get_cpu_var(vector_irq)[vector];
desc = irq_to_desc(irq);
spin_lock(&desc->lock);
if (desc->chip->retrigger)
desc->chip->retrigger(irq);
spin_unlock(&desc->lock);
}
}
}
#endif
...@@ -211,48 +211,3 @@ bool handle_irq(unsigned irq, struct pt_regs *regs) ...@@ -211,48 +211,3 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
return true; return true;
} }
#ifdef CONFIG_HOTPLUG_CPU
/* A cpu has been removed from cpu_online_mask. Reset irq affinities. */
void fixup_irqs(void)
{
unsigned int irq;
struct irq_desc *desc;
for_each_irq_desc(irq, desc) {
const struct cpumask *affinity;
if (!desc)
continue;
if (irq == 2)
continue;
affinity = desc->affinity;
if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
printk("Breaking affinity for irq %i\n", irq);
affinity = cpu_all_mask;
}
if (desc->chip->set_affinity)
desc->chip->set_affinity(irq, affinity);
else if (desc->action)
printk_once("Cannot set affinity for irq %i\n", irq);
}
#if 0
barrier();
/* Ingo Molnar says: "after the IO-APIC masks have been redirected
[note the nop - the interrupt-enable boundary on x86 is two
instructions from sti] - to flush out pending hardirqs and
IPIs. After this point nothing is supposed to reach this CPU." */
__asm__ __volatile__("sti; nop; cli");
barrier();
#else
/* That doesn't seem sufficient. Give it 1ms. */
local_irq_enable();
mdelay(1);
local_irq_disable();
#endif
}
#endif
...@@ -62,64 +62,6 @@ bool handle_irq(unsigned irq, struct pt_regs *regs) ...@@ -62,64 +62,6 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
return true; return true;
} }
#ifdef CONFIG_HOTPLUG_CPU
/* A cpu has been removed from cpu_online_mask. Reset irq affinities. */
void fixup_irqs(void)
{
unsigned int irq;
static int warned;
struct irq_desc *desc;
for_each_irq_desc(irq, desc) {
int break_affinity = 0;
int set_affinity = 1;
const struct cpumask *affinity;
if (!desc)
continue;
if (irq == 2)
continue;
/* interrupt's are disabled at this point */
spin_lock(&desc->lock);
affinity = desc->affinity;
if (!irq_has_action(irq) ||
cpumask_equal(affinity, cpu_online_mask)) {
spin_unlock(&desc->lock);
continue;
}
if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
break_affinity = 1;
affinity = cpu_all_mask;
}
if (desc->chip->mask)
desc->chip->mask(irq);
if (desc->chip->set_affinity)
desc->chip->set_affinity(irq, affinity);
else if (!(warned++))
set_affinity = 0;
if (desc->chip->unmask)
desc->chip->unmask(irq);
spin_unlock(&desc->lock);
if (break_affinity && set_affinity)
printk("Broke affinity for irq %i\n", irq);
else if (!set_affinity)
printk("Cannot set affinity for irq %i\n", irq);
}
/* That doesn't seem sufficient. Give it 1ms. */
local_irq_enable();
mdelay(1);
local_irq_disable();
}
#endif
extern void call_softirq(void); extern void call_softirq(void);
......
...@@ -1250,16 +1250,7 @@ static void __ref remove_cpu_from_maps(int cpu) ...@@ -1250,16 +1250,7 @@ static void __ref remove_cpu_from_maps(int cpu)
void cpu_disable_common(void) void cpu_disable_common(void)
{ {
int cpu = smp_processor_id(); int cpu = smp_processor_id();
/*
* HACK:
* Allow any queued timer interrupts to get serviced
* This is only a temporary solution until we cleanup
* fixup_irqs as we do for IA64.
*/
local_irq_enable();
mdelay(1);
local_irq_disable();
remove_siblinginfo(cpu); remove_siblinginfo(cpu);
/* It's now safe to remove this processor from the online map */ /* It's now safe to remove this processor from the online map */
......
...@@ -9,10 +9,25 @@ ...@@ -9,10 +9,25 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/rbtree.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/uv/uv_irq.h> #include <asm/uv/uv_irq.h>
#include <asm/uv/uv_hub.h>
/* MMR offset and pnode of hub sourcing interrupts for a given irq */
struct uv_irq_2_mmr_pnode{
struct rb_node list;
unsigned long offset;
int pnode;
int irq;
};
static spinlock_t uv_irq_lock;
static struct rb_root uv_irq_root;
static int uv_set_irq_affinity(unsigned int, const struct cpumask *);
static void uv_noop(unsigned int irq) static void uv_noop(unsigned int irq)
{ {
...@@ -39,25 +54,214 @@ struct irq_chip uv_irq_chip = { ...@@ -39,25 +54,214 @@ struct irq_chip uv_irq_chip = {
.unmask = uv_noop, .unmask = uv_noop,
.eoi = uv_ack_apic, .eoi = uv_ack_apic,
.end = uv_noop, .end = uv_noop,
.set_affinity = uv_set_irq_affinity,
}; };
/*
* Add offset and pnode information of the hub sourcing interrupts to the
* rb tree for a specific irq.
*/
static int uv_set_irq_2_mmr_info(int irq, unsigned long offset, unsigned blade)
{
struct rb_node **link = &uv_irq_root.rb_node;
struct rb_node *parent = NULL;
struct uv_irq_2_mmr_pnode *n;
struct uv_irq_2_mmr_pnode *e;
unsigned long irqflags;
n = kmalloc_node(sizeof(struct uv_irq_2_mmr_pnode), GFP_KERNEL,
uv_blade_to_memory_nid(blade));
if (!n)
return -ENOMEM;
n->irq = irq;
n->offset = offset;
n->pnode = uv_blade_to_pnode(blade);
spin_lock_irqsave(&uv_irq_lock, irqflags);
/* Find the right place in the rbtree: */
while (*link) {
parent = *link;
e = rb_entry(parent, struct uv_irq_2_mmr_pnode, list);
if (unlikely(irq == e->irq)) {
/* irq entry exists */
e->pnode = uv_blade_to_pnode(blade);
e->offset = offset;
spin_unlock_irqrestore(&uv_irq_lock, irqflags);
kfree(n);
return 0;
}
if (irq < e->irq)
link = &(*link)->rb_left;
else
link = &(*link)->rb_right;
}
/* Insert the node into the rbtree. */
rb_link_node(&n->list, parent, link);
rb_insert_color(&n->list, &uv_irq_root);
spin_unlock_irqrestore(&uv_irq_lock, irqflags);
return 0;
}
/* Retrieve offset and pnode information from the rb tree for a specific irq */
int uv_irq_2_mmr_info(int irq, unsigned long *offset, int *pnode)
{
struct uv_irq_2_mmr_pnode *e;
struct rb_node *n;
unsigned long irqflags;
spin_lock_irqsave(&uv_irq_lock, irqflags);
n = uv_irq_root.rb_node;
while (n) {
e = rb_entry(n, struct uv_irq_2_mmr_pnode, list);
if (e->irq == irq) {
*offset = e->offset;
*pnode = e->pnode;
spin_unlock_irqrestore(&uv_irq_lock, irqflags);
return 0;
}
if (irq < e->irq)
n = n->rb_left;
else
n = n->rb_right;
}
spin_unlock_irqrestore(&uv_irq_lock, irqflags);
return -1;
}
/*
* Re-target the irq to the specified CPU and enable the specified MMR located
* on the specified blade to allow the sending of MSIs to the specified CPU.
*/
static int
arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade,
unsigned long mmr_offset, int restrict)
{
const struct cpumask *eligible_cpu = cpumask_of(cpu);
struct irq_desc *desc = irq_to_desc(irq);
struct irq_cfg *cfg;
int mmr_pnode;
unsigned long mmr_value;
struct uv_IO_APIC_route_entry *entry;
int err;
BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) !=
sizeof(unsigned long));
cfg = irq_cfg(irq);
err = assign_irq_vector(irq, cfg, eligible_cpu);
if (err != 0)
return err;
if (restrict == UV_AFFINITY_CPU)
desc->status |= IRQ_NO_BALANCING;
else
desc->status |= IRQ_MOVE_PCNTXT;
set_irq_chip_and_handler_name(irq, &uv_irq_chip, handle_percpu_irq,
irq_name);
mmr_value = 0;
entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
entry->vector = cfg->vector;
entry->delivery_mode = apic->irq_delivery_mode;
entry->dest_mode = apic->irq_dest_mode;
entry->polarity = 0;
entry->trigger = 0;
entry->mask = 0;
entry->dest = apic->cpu_mask_to_apicid(eligible_cpu);
mmr_pnode = uv_blade_to_pnode(mmr_blade);
uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
if (cfg->move_in_progress)
send_cleanup_vector(cfg);
return irq;
}
/*
* Disable the specified MMR located on the specified blade so that MSIs are
* longer allowed to be sent.
*/
static void arch_disable_uv_irq(int mmr_pnode, unsigned long mmr_offset)
{
unsigned long mmr_value;
struct uv_IO_APIC_route_entry *entry;
BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) !=
sizeof(unsigned long));
mmr_value = 0;
entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
entry->mask = 1;
uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
}
static int uv_set_irq_affinity(unsigned int irq, const struct cpumask *mask)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irq_cfg *cfg = desc->chip_data;
unsigned int dest;
unsigned long mmr_value;
struct uv_IO_APIC_route_entry *entry;
unsigned long mmr_offset;
unsigned mmr_pnode;
dest = set_desc_affinity(desc, mask);
if (dest == BAD_APICID)
return -1;
mmr_value = 0;
entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
entry->vector = cfg->vector;
entry->delivery_mode = apic->irq_delivery_mode;
entry->dest_mode = apic->irq_dest_mode;
entry->polarity = 0;
entry->trigger = 0;
entry->mask = 0;
entry->dest = dest;
/* Get previously stored MMR and pnode of hub sourcing interrupts */
if (uv_irq_2_mmr_info(irq, &mmr_offset, &mmr_pnode))
return -1;
uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
if (cfg->move_in_progress)
send_cleanup_vector(cfg);
return 0;
}
/* /*
* Set up a mapping of an available irq and vector, and enable the specified * Set up a mapping of an available irq and vector, and enable the specified
* MMR that defines the MSI that is to be sent to the specified CPU when an * MMR that defines the MSI that is to be sent to the specified CPU when an
* interrupt is raised. * interrupt is raised.
*/ */
int uv_setup_irq(char *irq_name, int cpu, int mmr_blade, int uv_setup_irq(char *irq_name, int cpu, int mmr_blade,
unsigned long mmr_offset) unsigned long mmr_offset, int restrict)
{ {
int irq; int irq, ret;
int ret;
irq = create_irq_nr(NR_IRQS_LEGACY, uv_blade_to_memory_nid(mmr_blade));
irq = create_irq();
if (irq <= 0) if (irq <= 0)
return -EBUSY; return -EBUSY;
ret = arch_enable_uv_irq(irq_name, irq, cpu, mmr_blade, mmr_offset); ret = arch_enable_uv_irq(irq_name, irq, cpu, mmr_blade, mmr_offset,
if (ret != irq) restrict);
if (ret == irq)
uv_set_irq_2_mmr_info(irq, mmr_offset, mmr_blade);
else
destroy_irq(irq); destroy_irq(irq);
return ret; return ret;
...@@ -71,9 +275,28 @@ EXPORT_SYMBOL_GPL(uv_setup_irq); ...@@ -71,9 +275,28 @@ EXPORT_SYMBOL_GPL(uv_setup_irq);
* *
* Set mmr_blade and mmr_offset to what was passed in on uv_setup_irq(). * Set mmr_blade and mmr_offset to what was passed in on uv_setup_irq().
*/ */
void uv_teardown_irq(unsigned int irq, int mmr_blade, unsigned long mmr_offset) void uv_teardown_irq(unsigned int irq)
{ {
arch_disable_uv_irq(mmr_blade, mmr_offset); struct uv_irq_2_mmr_pnode *e;
struct rb_node *n;
unsigned long irqflags;
spin_lock_irqsave(&uv_irq_lock, irqflags);
n = uv_irq_root.rb_node;
while (n) {
e = rb_entry(n, struct uv_irq_2_mmr_pnode, list);
if (e->irq == irq) {
arch_disable_uv_irq(e->pnode, e->offset);
rb_erase(n, &uv_irq_root);
kfree(e);
break;
}
if (irq < e->irq)
n = n->rb_left;
else
n = n->rb_right;
}
spin_unlock_irqrestore(&uv_irq_lock, irqflags);
destroy_irq(irq); destroy_irq(irq);
} }
EXPORT_SYMBOL_GPL(uv_teardown_irq); EXPORT_SYMBOL_GPL(uv_teardown_irq);
...@@ -183,7 +183,7 @@ static void __init MP_processor_info(struct mpc_cpu *m) ...@@ -183,7 +183,7 @@ static void __init MP_processor_info(struct mpc_cpu *m)
return; return;
} }
apic_cpus = apic->apicid_to_cpu_present(m->apicid); apic->apicid_to_cpu_present(m->apicid, &apic_cpus);
physids_or(phys_cpu_present_map, phys_cpu_present_map, apic_cpus); physids_or(phys_cpu_present_map, phys_cpu_present_map, apic_cpus);
/* /*
* Validate version * Validate version
......
...@@ -136,7 +136,7 @@ acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) ...@@ -136,7 +136,7 @@ acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
apicid_to_node[apic_id] = node; apicid_to_node[apic_id] = node;
node_set(node, cpu_nodes_parsed); node_set(node, cpu_nodes_parsed);
acpi_numa = 1; acpi_numa = 1;
printk(KERN_INFO "SRAT: PXM %u -> APIC %u -> Node %u\n", printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n",
pxm, apic_id, node); pxm, apic_id, node);
} }
...@@ -170,7 +170,7 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) ...@@ -170,7 +170,7 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
apicid_to_node[apic_id] = node; apicid_to_node[apic_id] = node;
node_set(node, cpu_nodes_parsed); node_set(node, cpu_nodes_parsed);
acpi_numa = 1; acpi_numa = 1;
printk(KERN_INFO "SRAT: PXM %u -> APIC %u -> Node %u\n", printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n",
pxm, apic_id, node); pxm, apic_id, node);
} }
......
...@@ -106,7 +106,8 @@ xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name) ...@@ -106,7 +106,8 @@ xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name)
int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade); int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
#if defined CONFIG_X86_64 #if defined CONFIG_X86_64
mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset); mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset,
UV_AFFINITY_CPU);
if (mq->irq < 0) { if (mq->irq < 0) {
dev_err(xpc_part, "uv_setup_irq() returned error=%d\n", dev_err(xpc_part, "uv_setup_irq() returned error=%d\n",
-mq->irq); -mq->irq);
...@@ -136,7 +137,7 @@ static void ...@@ -136,7 +137,7 @@ static void
xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq) xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq)
{ {
#if defined CONFIG_X86_64 #if defined CONFIG_X86_64
uv_teardown_irq(mq->irq, mq->mmr_blade, mq->mmr_offset); uv_teardown_irq(mq->irq);
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV #elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
int mmr_pnode; int mmr_pnode;
......
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