diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 1fe841a86f7ed369e75856ba215a94c77f78c667..0bf2fb55aa7415cb824faf011a86facc29286282 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -89,7 +89,7 @@ scx200-y += scx200_32.o ### # 64 bit specific files ifeq ($(CONFIG_X86_64),y) - obj-y += genapic_64.o genapic_flat_64.o + obj-y += genapic_64.o genapic_flat_64.o genx2apic_uv_x.o obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o obj-$(CONFIG_AUDIT) += audit_64.o diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 9b4cacdfd74fdb4a83fa7db0619e0fbac3754bd5..8b0fad47a5d2952063e35fdd91d45feb5f24f692 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -738,6 +738,7 @@ void __cpuinit setup_local_APIC(void) unsigned int value; int i, j; + preempt_disable(); value = apic_read(APIC_LVR); BUILD_BUG_ON((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f); @@ -831,6 +832,7 @@ void __cpuinit setup_local_APIC(void) else value = APIC_DM_NMI | APIC_LVT_MASKED; apic_write(APIC_LVT1, value); + preempt_enable(); } void __cpuinit lapic_setup_esr(void) diff --git a/arch/x86/kernel/genapic_64.c b/arch/x86/kernel/genapic_64.c index 4cc1c218ae4c0eeeee7d1bde524a219bf1cf4e63..910a4a777a4c26d668f00b7340faf5368e1c7300 100644 --- a/arch/x86/kernel/genapic_64.c +++ b/arch/x86/kernel/genapic_64.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ void *x86_cpu_to_apicid_early_ptr; #endif DEFINE_PER_CPU(u16, x86_cpu_to_apicid) = BAD_APICID; EXPORT_PER_CPU_SYMBOL(x86_cpu_to_apicid); +DEFINE_PER_CPU(int, x2apic_extra_bits); struct genapic __read_mostly *genapic = &apic_flat; @@ -42,6 +44,9 @@ static enum uv_system_type uv_system_type; */ void __init setup_apic_routing(void) { + if (uv_system_type == UV_NON_UNIQUE_APIC) + genapic = &apic_x2apic_uv_x; + else #ifdef CONFIG_ACPI /* * Quirk: some x86_64 machines can only use physical APIC mode @@ -82,6 +87,19 @@ int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) return 0; } +unsigned int read_apic_id(void) +{ + unsigned int id; + + WARN_ON(preemptible()); + id = apic_read(APIC_ID); + if (uv_system_type >= UV_X2APIC) + id |= __get_cpu_var(x2apic_extra_bits); + else + id = (id >> 24) & 0xFFu;; + return id; +} + enum uv_system_type get_uv_system_type(void) { return uv_system_type; diff --git a/arch/x86/kernel/genx2apic_uv_x.c b/arch/x86/kernel/genx2apic_uv_x.c new file mode 100644 index 0000000000000000000000000000000000000000..5d77c9cd8e15c6782e4a0872d5f8e4e820509395 --- /dev/null +++ b/arch/x86/kernel/genx2apic_uv_x.c @@ -0,0 +1,245 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * SGI UV APIC functions (note: not an Intel compatible APIC) + * + * Copyright (C) 2007 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DEFINE_PER_CPU(struct uv_hub_info_s, __uv_hub_info); +EXPORT_PER_CPU_SYMBOL_GPL(__uv_hub_info); + +struct uv_blade_info *uv_blade_info; +EXPORT_SYMBOL_GPL(uv_blade_info); + +short *uv_node_to_blade; +EXPORT_SYMBOL_GPL(uv_node_to_blade); + +short *uv_cpu_to_blade; +EXPORT_SYMBOL_GPL(uv_cpu_to_blade); + +short uv_possible_blades; +EXPORT_SYMBOL_GPL(uv_possible_blades); + +/* Start with all IRQs pointing to boot CPU. IRQ balancing will shift them. */ + +static cpumask_t uv_target_cpus(void) +{ + return cpumask_of_cpu(0); +} + +static cpumask_t uv_vector_allocation_domain(int cpu) +{ + cpumask_t domain = CPU_MASK_NONE; + cpu_set(cpu, domain); + return domain; +} + +int uv_wakeup_secondary(int phys_apicid, unsigned int start_rip) +{ + unsigned long val; + int nasid; + + nasid = uv_apicid_to_nasid(phys_apicid); + val = (1UL << UVH_IPI_INT_SEND_SHFT) | + (phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) | + (((long)start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) | + (6 << UVH_IPI_INT_DELIVERY_MODE_SHFT); + uv_write_global_mmr64(nasid, UVH_IPI_INT, val); + return 0; +} + +static void uv_send_IPI_one(int cpu, int vector) +{ + unsigned long val, apicid; + int nasid; + + apicid = per_cpu(x86_cpu_to_apicid, cpu); /* ZZZ - cache node-local ? */ + nasid = uv_apicid_to_nasid(apicid); + val = + (1UL << UVH_IPI_INT_SEND_SHFT) | (apicid << + UVH_IPI_INT_APIC_ID_SHFT) | + (vector << UVH_IPI_INT_VECTOR_SHFT); + uv_write_global_mmr64(nasid, UVH_IPI_INT, val); + printk(KERN_DEBUG + "UV: IPI to cpu %d, apicid 0x%lx, vec %d, nasid%d, val 0x%lx\n", + cpu, apicid, vector, nasid, val); +} + +static void uv_send_IPI_mask(cpumask_t mask, int vector) +{ + unsigned int cpu; + + for (cpu = 0; cpu < NR_CPUS; ++cpu) + if (cpu_isset(cpu, mask)) + uv_send_IPI_one(cpu, vector); +} + +static void uv_send_IPI_allbutself(int vector) +{ + cpumask_t mask = cpu_online_map; + + cpu_clear(smp_processor_id(), mask); + + if (!cpus_empty(mask)) + uv_send_IPI_mask(mask, vector); +} + +static void uv_send_IPI_all(int vector) +{ + uv_send_IPI_mask(cpu_online_map, vector); +} + +static int uv_apic_id_registered(void) +{ + return 1; +} + +static unsigned int uv_cpu_mask_to_apicid(cpumask_t cpumask) +{ + int cpu; + + /* + * We're using fixed IRQ delivery, can only return one phys APIC ID. + * May as well be the first. + */ + cpu = first_cpu(cpumask); + if ((unsigned)cpu < NR_CPUS) + return per_cpu(x86_cpu_to_apicid, cpu); + else + return BAD_APICID; +} + +static unsigned int phys_pkg_id(int index_msb) +{ + return GET_APIC_ID(read_apic_id()) >> index_msb; +} + +#ifdef ZZZ /* Needs x2apic patch */ +static void uv_send_IPI_self(int vector) +{ + apic_write(APIC_SELF_IPI, vector); +} +#endif + +struct genapic apic_x2apic_uv_x = { + .name = "UV large system", + .int_delivery_mode = dest_Fixed, + .int_dest_mode = (APIC_DEST_PHYSICAL != 0), + .target_cpus = uv_target_cpus, + .vector_allocation_domain = uv_vector_allocation_domain,/* Fixme ZZZ */ + .apic_id_registered = uv_apic_id_registered, + .send_IPI_all = uv_send_IPI_all, + .send_IPI_allbutself = uv_send_IPI_allbutself, + .send_IPI_mask = uv_send_IPI_mask, + /* ZZZ.send_IPI_self = uv_send_IPI_self, */ + .cpu_mask_to_apicid = uv_cpu_mask_to_apicid, + .phys_pkg_id = phys_pkg_id, /* Fixme ZZZ */ +}; + +static __cpuinit void set_x2apic_extra_bits(int nasid) +{ + __get_cpu_var(x2apic_extra_bits) = ((nasid >> 1) << 6); +} + +/* + * Called on boot cpu. + */ +static __init void uv_system_init(void) +{ + union uvh_si_addr_map_config_u m_n_config; + int bytes, nid, cpu, lcpu, nasid, last_nasid, blade; + unsigned long mmr_base; + + m_n_config.v = uv_read_local_mmr(UVH_SI_ADDR_MAP_CONFIG); + mmr_base = + uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR) & + ~UV_MMR_ENABLE; + printk(KERN_DEBUG "UV: global MMR base 0x%lx\n", mmr_base); + + last_nasid = -1; + for_each_possible_cpu(cpu) { + nid = cpu_to_node(cpu); + 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()); + + bytes = sizeof(struct uv_blade_info) * uv_num_possible_blades(); + uv_blade_info = alloc_bootmem_pages(bytes); + + bytes = sizeof(uv_node_to_blade[0]) * num_possible_nodes(); + uv_node_to_blade = alloc_bootmem_pages(bytes); + memset(uv_node_to_blade, 255, bytes); + + bytes = sizeof(uv_cpu_to_blade[0]) * num_possible_cpus(); + uv_cpu_to_blade = alloc_bootmem_pages(bytes); + memset(uv_cpu_to_blade, 255, bytes); + + last_nasid = -1; + blade = -1; + lcpu = -1; + for_each_possible_cpu(cpu) { + nid = cpu_to_node(cpu); + nasid = uv_apicid_to_nasid(per_cpu(x86_cpu_to_apicid, cpu)); + if (nasid != last_nasid) { + blade++; + lcpu = -1; + uv_blade_info[blade].nr_posible_cpus = 0; + uv_blade_info[blade].nr_online_cpus = 0; + } + last_nasid = nasid; + lcpu++; + + uv_cpu_hub_info(cpu)->m_val = m_n_config.s.m_skt; + uv_cpu_hub_info(cpu)->n_val = m_n_config.s.n_skt; + uv_cpu_hub_info(cpu)->numa_blade_id = blade; + uv_cpu_hub_info(cpu)->blade_processor_id = lcpu; + uv_cpu_hub_info(cpu)->local_nasid = nasid; + uv_cpu_hub_info(cpu)->gnode_upper = + nasid & ~((1 << uv_hub_info->n_val) - 1); + uv_cpu_hub_info(cpu)->global_mmr_base = mmr_base; + 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_cpu_to_blade[cpu] = blade; + + printk(KERN_DEBUG "UV cpu %d, apicid 0x%x, nasid %d, nid %d\n", + cpu, per_cpu(x86_cpu_to_apicid, cpu), nasid, nid); + printk(KERN_DEBUG "UV lcpu %d, blade %d\n", lcpu, blade); + } +} + +/* + * Called on each cpu to initialize the per_cpu UV data area. + */ +void __cpuinit uv_cpu_init(void) +{ + if (!uv_node_to_blade) + uv_system_init(); + + uv_blade_info[uv_numa_blade_id()].nr_online_cpus++; + + if (get_uv_system_type() == UV_NON_UNIQUE_APIC) + set_x2apic_extra_bits(uv_hub_info->local_nasid); +} diff --git a/arch/x86/kernel/setup64.c b/arch/x86/kernel/setup64.c index 6b4e3262e8cb88e068ce0636f075f176dd2b4dd9..4be499cd6a0d3de1f43f3296d113149a48b770e1 100644 --- a/arch/x86/kernel/setup64.c +++ b/arch/x86/kernel/setup64.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifndef CONFIG_DEBUG_BOOT_PARAMS struct boot_params __initdata boot_params; @@ -264,4 +265,7 @@ void __cpuinit cpu_init (void) fpu_init(); raw_local_save_flags(kernel_eflags); + + if (is_uv_system()) + uv_cpu_init(); } diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 5da35d2cdbd8073b6db9f20458be59fccb382b74..22bf6c29454f42f59a43bf21189a054f20edd0d4 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1101,6 +1101,7 @@ static __init void disable_smp(void) */ static int __init smp_sanity_check(unsigned max_cpus) { + preempt_disable(); if (!physid_isset(hard_smp_processor_id(), phys_cpu_present_map)) { printk(KERN_WARNING "weird, boot CPU (#%d) not listed" "by the BIOS.\n", hard_smp_processor_id()); @@ -1112,6 +1113,7 @@ static int __init smp_sanity_check(unsigned max_cpus) * get out of here now! */ if (!smp_found_config && !acpi_lapic) { + preempt_enable(); printk(KERN_NOTICE "SMP motherboard not detected.\n"); disable_smp(); if (APIC_init_uniprocessor()) @@ -1130,6 +1132,7 @@ static int __init smp_sanity_check(unsigned max_cpus) boot_cpu_physical_apicid); physid_set(hard_smp_processor_id(), phys_cpu_present_map); } + preempt_enable(); /* * If we couldn't find a local APIC, then get out of here now! @@ -1205,11 +1208,13 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus) return; } + preempt_disable(); if (GET_APIC_ID(read_apic_id()) != boot_cpu_physical_apicid) { panic("Boot APIC ID in local APIC unexpected (%d vs %d)", GET_APIC_ID(read_apic_id()), boot_cpu_physical_apicid); /* Or can we switch back to PIC here? */ } + preempt_enable(); #ifdef CONFIG_X86_32 connect_bsp_APIC(); diff --git a/include/asm-x86/genapic_64.h b/include/asm-x86/genapic_64.h index 914815c28ae52469350701d5822474d24a34970d..1de931b263ce142f7b30ae70af6c033cbd3f25d5 100644 --- a/include/asm-x86/genapic_64.h +++ b/include/asm-x86/genapic_64.h @@ -39,4 +39,9 @@ enum uv_system_type {UV_NONE, UV_LEGACY_APIC, UV_X2APIC, UV_NON_UNIQUE_APIC}; extern enum uv_system_type get_uv_system_type(void); extern int is_uv_system(void); +extern struct genapic apic_x2apic_uv_x; +DECLARE_PER_CPU(int, x2apic_extra_bits); +extern void uv_cpu_init(void); +extern int uv_wakeup_secondary(int phys_apicid, unsigned int start_rip); + #endif diff --git a/include/asm-x86/smp.h b/include/asm-x86/smp.h index c0d693ca43576724ad2b19ef5e3c4be3ab72a6fb..b35566264879d0e22864839085f8f3a438e9ee28 100644 --- a/include/asm-x86/smp.h +++ b/include/asm-x86/smp.h @@ -174,10 +174,15 @@ static inline int logical_smp_processor_id(void) return GET_APIC_LOGICAL_ID(*(u32 *)(APIC_BASE + APIC_LDR)); } +#ifdef CONFIG_X86_32_SMP static inline unsigned int read_apic_id(void) { return *(u32 *)(APIC_BASE + APIC_ID); } +#else +extern unsigned int read_apic_id(void); +#endif + # ifdef APIC_DEFINITION extern int hard_smp_processor_id(void);