aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJack Steiner <steiner@sgi.com>2008-03-28 15:12:16 -0400
committerIngo Molnar <mingo@elte.hu>2008-04-17 11:41:33 -0400
commitac23d4ee3f84de33c16ed7e68f9adee2386e74fb (patch)
tree296346293480fb5d67a15d7552bf41bd0cadd4cf /arch
parent570da318cf0e3053e62030253494c410a18d4be7 (diff)
x86: support for new UV apic
UV supports really big systems. So big, in fact, that the APICID register does not contain enough bits to contain an APICID that is unique across all cpus. The UV BIOS supports 3 APICID modes: - legacy mode. This mode uses the old APIC mode where APICID is in bits [31:24] of the APICID register. - x2apic mode. This mode is whitebox-compatible. APICIDs are unique across all cpus. Standard x2apic APIC operations (Intel-defined) can be used for IPIs. The node identifier fits within the Intel-defined portion of the APICID register. - x2apic-uv mode. In this mode, the APICIDs on each node have unique IDs, but IDs on different node are not unique. For example, if each mode has 32 cpus, the APICIDs on each node might be 0 - 31. Every node has the same set of IDs. The UV hub is used to route IPIs/interrupts to the correct node. Traditional APIC operations WILL NOT WORK. In x2apic-uv mode, the ACPI tables all contain a full unique ID (note: exact bit layout still changing but the following is close): nnnnnnnnnnlc0cch n = unique node number l = socket number on board c = core h = hyperthread Only the "lc0cch" bits are written to the APICID register. The remaining bits are supplied by having the get_apic_id() function "OR" the extra bits into the value read from the APICID register. (Hmmm.. why not keep the ENTIRE APICID register in per-cpu data....) The x2apic-uv mode is recognized by the MADT table containing: oem_id = "SGI" oem_table_id = "UV-X" Signed-off-by: Jack Steiner <steiner@sgi.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/Makefile2
-rw-r--r--arch/x86/kernel/apic_64.c2
-rw-r--r--arch/x86/kernel/genapic_64.c18
-rw-r--r--arch/x86/kernel/genx2apic_uv_x.c245
-rw-r--r--arch/x86/kernel/setup64.c4
-rw-r--r--arch/x86/kernel/smpboot.c5
6 files changed, 275 insertions, 1 deletions
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 1fe841a86f7e..0bf2fb55aa74 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -89,7 +89,7 @@ scx200-y += scx200_32.o
89### 89###
90# 64 bit specific files 90# 64 bit specific files
91ifeq ($(CONFIG_X86_64),y) 91ifeq ($(CONFIG_X86_64),y)
92 obj-y += genapic_64.o genapic_flat_64.o 92 obj-y += genapic_64.o genapic_flat_64.o genx2apic_uv_x.o
93 obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o 93 obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o
94 obj-$(CONFIG_AUDIT) += audit_64.o 94 obj-$(CONFIG_AUDIT) += audit_64.o
95 95
diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c
index 9b4cacdfd74f..8b0fad47a5d2 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)
738 unsigned int value; 738 unsigned int value;
739 int i, j; 739 int i, j;
740 740
741 preempt_disable();
741 value = apic_read(APIC_LVR); 742 value = apic_read(APIC_LVR);
742 743
743 BUILD_BUG_ON((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f); 744 BUILD_BUG_ON((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f);
@@ -831,6 +832,7 @@ void __cpuinit setup_local_APIC(void)
831 else 832 else
832 value = APIC_DM_NMI | APIC_LVT_MASKED; 833 value = APIC_DM_NMI | APIC_LVT_MASKED;
833 apic_write(APIC_LVT1, value); 834 apic_write(APIC_LVT1, value);
835 preempt_enable();
834} 836}
835 837
836void __cpuinit lapic_setup_esr(void) 838void __cpuinit lapic_setup_esr(void)
diff --git a/arch/x86/kernel/genapic_64.c b/arch/x86/kernel/genapic_64.c
index 4cc1c218ae4c..910a4a777a4c 100644
--- a/arch/x86/kernel/genapic_64.c
+++ b/arch/x86/kernel/genapic_64.c
@@ -15,6 +15,7 @@
15#include <linux/kernel.h> 15#include <linux/kernel.h>
16#include <linux/ctype.h> 16#include <linux/ctype.h>
17#include <linux/init.h> 17#include <linux/init.h>
18#include <linux/hardirq.h>
18 19
19#include <asm/smp.h> 20#include <asm/smp.h>
20#include <asm/ipi.h> 21#include <asm/ipi.h>
@@ -32,6 +33,7 @@ void *x86_cpu_to_apicid_early_ptr;
32#endif 33#endif
33DEFINE_PER_CPU(u16, x86_cpu_to_apicid) = BAD_APICID; 34DEFINE_PER_CPU(u16, x86_cpu_to_apicid) = BAD_APICID;
34EXPORT_PER_CPU_SYMBOL(x86_cpu_to_apicid); 35EXPORT_PER_CPU_SYMBOL(x86_cpu_to_apicid);
36DEFINE_PER_CPU(int, x2apic_extra_bits);
35 37
36struct genapic __read_mostly *genapic = &apic_flat; 38struct genapic __read_mostly *genapic = &apic_flat;
37 39
@@ -42,6 +44,9 @@ static enum uv_system_type uv_system_type;
42 */ 44 */
43void __init setup_apic_routing(void) 45void __init setup_apic_routing(void)
44{ 46{
47 if (uv_system_type == UV_NON_UNIQUE_APIC)
48 genapic = &apic_x2apic_uv_x;
49 else
45#ifdef CONFIG_ACPI 50#ifdef CONFIG_ACPI
46 /* 51 /*
47 * Quirk: some x86_64 machines can only use physical APIC mode 52 * 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)
82 return 0; 87 return 0;
83} 88}
84 89
90unsigned int read_apic_id(void)
91{
92 unsigned int id;
93
94 WARN_ON(preemptible());
95 id = apic_read(APIC_ID);
96 if (uv_system_type >= UV_X2APIC)
97 id |= __get_cpu_var(x2apic_extra_bits);
98 else
99 id = (id >> 24) & 0xFFu;;
100 return id;
101}
102
85enum uv_system_type get_uv_system_type(void) 103enum uv_system_type get_uv_system_type(void)
86{ 104{
87 return uv_system_type; 105 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 000000000000..5d77c9cd8e15
--- /dev/null
+++ b/arch/x86/kernel/genx2apic_uv_x.c
@@ -0,0 +1,245 @@
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * SGI UV APIC functions (note: not an Intel compatible APIC)
7 *
8 * Copyright (C) 2007 Silicon Graphics, Inc. All rights reserved.
9 */
10
11#include <linux/threads.h>
12#include <linux/cpumask.h>
13#include <linux/string.h>
14#include <linux/kernel.h>
15#include <linux/ctype.h>
16#include <linux/init.h>
17#include <linux/sched.h>
18#include <linux/bootmem.h>
19#include <linux/module.h>
20#include <asm/smp.h>
21#include <asm/ipi.h>
22#include <asm/genapic.h>
23#include <asm/uv/uv_mmrs.h>
24#include <asm/uv/uv_hub.h>
25
26DEFINE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
27EXPORT_PER_CPU_SYMBOL_GPL(__uv_hub_info);
28
29struct uv_blade_info *uv_blade_info;
30EXPORT_SYMBOL_GPL(uv_blade_info);
31
32short *uv_node_to_blade;
33EXPORT_SYMBOL_GPL(uv_node_to_blade);
34
35short *uv_cpu_to_blade;
36EXPORT_SYMBOL_GPL(uv_cpu_to_blade);
37
38short uv_possible_blades;
39EXPORT_SYMBOL_GPL(uv_possible_blades);
40
41/* Start with all IRQs pointing to boot CPU. IRQ balancing will shift them. */
42
43static cpumask_t uv_target_cpus(void)
44{
45 return cpumask_of_cpu(0);
46}
47
48static cpumask_t uv_vector_allocation_domain(int cpu)
49{
50 cpumask_t domain = CPU_MASK_NONE;
51 cpu_set(cpu, domain);
52 return domain;
53}
54
55int uv_wakeup_secondary(int phys_apicid, unsigned int start_rip)
56{
57 unsigned long val;
58 int nasid;
59
60 nasid = uv_apicid_to_nasid(phys_apicid);
61 val = (1UL << UVH_IPI_INT_SEND_SHFT) |
62 (phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) |
63 (((long)start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) |
64 (6 << UVH_IPI_INT_DELIVERY_MODE_SHFT);
65 uv_write_global_mmr64(nasid, UVH_IPI_INT, val);
66 return 0;
67}
68
69static void uv_send_IPI_one(int cpu, int vector)
70{
71 unsigned long val, apicid;
72 int nasid;
73
74 apicid = per_cpu(x86_cpu_to_apicid, cpu); /* ZZZ - cache node-local ? */
75 nasid = uv_apicid_to_nasid(apicid);
76 val =
77 (1UL << UVH_IPI_INT_SEND_SHFT) | (apicid <<
78 UVH_IPI_INT_APIC_ID_SHFT) |
79 (vector << UVH_IPI_INT_VECTOR_SHFT);
80 uv_write_global_mmr64(nasid, UVH_IPI_INT, val);
81 printk(KERN_DEBUG
82 "UV: IPI to cpu %d, apicid 0x%lx, vec %d, nasid%d, val 0x%lx\n",
83 cpu, apicid, vector, nasid, val);
84}
85
86static void uv_send_IPI_mask(cpumask_t mask, int vector)
87{
88 unsigned int cpu;
89
90 for (cpu = 0; cpu < NR_CPUS; ++cpu)
91 if (cpu_isset(cpu, mask))
92 uv_send_IPI_one(cpu, vector);
93}
94
95static void uv_send_IPI_allbutself(int vector)
96{
97 cpumask_t mask = cpu_online_map;
98
99 cpu_clear(smp_processor_id(), mask);
100
101 if (!cpus_empty(mask))
102 uv_send_IPI_mask(mask, vector);
103}
104
105static void uv_send_IPI_all(int vector)
106{
107 uv_send_IPI_mask(cpu_online_map, vector);
108}
109
110static int uv_apic_id_registered(void)
111{
112 return 1;
113}
114
115static unsigned int uv_cpu_mask_to_apicid(cpumask_t cpumask)
116{
117 int cpu;
118
119 /*
120 * We're using fixed IRQ delivery, can only return one phys APIC ID.
121 * May as well be the first.
122 */
123 cpu = first_cpu(cpumask);
124 if ((unsigned)cpu < NR_CPUS)
125 return per_cpu(x86_cpu_to_apicid, cpu);
126 else
127 return BAD_APICID;
128}
129
130static unsigned int phys_pkg_id(int index_msb)
131{
132 return GET_APIC_ID(read_apic_id()) >> index_msb;
133}
134
135#ifdef ZZZ /* Needs x2apic patch */
136static void uv_send_IPI_self(int vector)
137{
138 apic_write(APIC_SELF_IPI, vector);
139}
140#endif
141
142struct genapic apic_x2apic_uv_x = {
143 .name = "UV large system",
144 .int_delivery_mode = dest_Fixed,
145 .int_dest_mode = (APIC_DEST_PHYSICAL != 0),
146 .target_cpus = uv_target_cpus,
147 .vector_allocation_domain = uv_vector_allocation_domain,/* Fixme ZZZ */
148 .apic_id_registered = uv_apic_id_registered,
149 .send_IPI_all = uv_send_IPI_all,
150 .send_IPI_allbutself = uv_send_IPI_allbutself,
151 .send_IPI_mask = uv_send_IPI_mask,
152 /* ZZZ.send_IPI_self = uv_send_IPI_self, */
153 .cpu_mask_to_apicid = uv_cpu_mask_to_apicid,
154 .phys_pkg_id = phys_pkg_id, /* Fixme ZZZ */
155};
156
157static __cpuinit void set_x2apic_extra_bits(int nasid)
158{
159 __get_cpu_var(x2apic_extra_bits) = ((nasid >> 1) << 6);
160}
161
162/*
163 * Called on boot cpu.
164 */
165static __init void uv_system_init(void)
166{
167 union uvh_si_addr_map_config_u m_n_config;
168 int bytes, nid, cpu, lcpu, nasid, last_nasid, blade;
169 unsigned long mmr_base;
170
171 m_n_config.v = uv_read_local_mmr(UVH_SI_ADDR_MAP_CONFIG);
172 mmr_base =
173 uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR) &
174 ~UV_MMR_ENABLE;
175 printk(KERN_DEBUG "UV: global MMR base 0x%lx\n", mmr_base);
176
177 last_nasid = -1;
178 for_each_possible_cpu(cpu) {
179 nid = cpu_to_node(cpu);
180 nasid = uv_apicid_to_nasid(per_cpu(x86_cpu_to_apicid, cpu));
181 if (nasid != last_nasid)
182 uv_possible_blades++;
183 last_nasid = nasid;
184 }
185 printk(KERN_DEBUG "UV: Found %d blades\n", uv_num_possible_blades());
186
187 bytes = sizeof(struct uv_blade_info) * uv_num_possible_blades();
188 uv_blade_info = alloc_bootmem_pages(bytes);
189
190 bytes = sizeof(uv_node_to_blade[0]) * num_possible_nodes();
191 uv_node_to_blade = alloc_bootmem_pages(bytes);
192 memset(uv_node_to_blade, 255, bytes);
193
194 bytes = sizeof(uv_cpu_to_blade[0]) * num_possible_cpus();
195 uv_cpu_to_blade = alloc_bootmem_pages(bytes);
196 memset(uv_cpu_to_blade, 255, bytes);
197
198 last_nasid = -1;
199 blade = -1;
200 lcpu = -1;
201 for_each_possible_cpu(cpu) {
202 nid = cpu_to_node(cpu);
203 nasid = uv_apicid_to_nasid(per_cpu(x86_cpu_to_apicid, cpu));
204 if (nasid != last_nasid) {
205 blade++;
206 lcpu = -1;
207 uv_blade_info[blade].nr_posible_cpus = 0;
208 uv_blade_info[blade].nr_online_cpus = 0;
209 }
210 last_nasid = nasid;
211 lcpu++;
212
213 uv_cpu_hub_info(cpu)->m_val = m_n_config.s.m_skt;
214 uv_cpu_hub_info(cpu)->n_val = m_n_config.s.n_skt;
215 uv_cpu_hub_info(cpu)->numa_blade_id = blade;
216 uv_cpu_hub_info(cpu)->blade_processor_id = lcpu;
217 uv_cpu_hub_info(cpu)->local_nasid = nasid;
218 uv_cpu_hub_info(cpu)->gnode_upper =
219 nasid & ~((1 << uv_hub_info->n_val) - 1);
220 uv_cpu_hub_info(cpu)->global_mmr_base = mmr_base;
221 uv_cpu_hub_info(cpu)->coherency_domain_number = 0;/* ZZZ */
222 uv_blade_info[blade].nasid = nasid;
223 uv_blade_info[blade].nr_posible_cpus++;
224 uv_node_to_blade[nid] = blade;
225 uv_cpu_to_blade[cpu] = blade;
226
227 printk(KERN_DEBUG "UV cpu %d, apicid 0x%x, nasid %d, nid %d\n",
228 cpu, per_cpu(x86_cpu_to_apicid, cpu), nasid, nid);
229 printk(KERN_DEBUG "UV lcpu %d, blade %d\n", lcpu, blade);
230 }
231}
232
233/*
234 * Called on each cpu to initialize the per_cpu UV data area.
235 */
236void __cpuinit uv_cpu_init(void)
237{
238 if (!uv_node_to_blade)
239 uv_system_init();
240
241 uv_blade_info[uv_numa_blade_id()].nr_online_cpus++;
242
243 if (get_uv_system_type() == UV_NON_UNIQUE_APIC)
244 set_x2apic_extra_bits(uv_hub_info->local_nasid);
245}
diff --git a/arch/x86/kernel/setup64.c b/arch/x86/kernel/setup64.c
index 6b4e3262e8cb..4be499cd6a0d 100644
--- a/arch/x86/kernel/setup64.c
+++ b/arch/x86/kernel/setup64.c
@@ -23,6 +23,7 @@
23#include <asm/proto.h> 23#include <asm/proto.h>
24#include <asm/sections.h> 24#include <asm/sections.h>
25#include <asm/setup.h> 25#include <asm/setup.h>
26#include <asm/genapic.h>
26 27
27#ifndef CONFIG_DEBUG_BOOT_PARAMS 28#ifndef CONFIG_DEBUG_BOOT_PARAMS
28struct boot_params __initdata boot_params; 29struct boot_params __initdata boot_params;
@@ -264,4 +265,7 @@ void __cpuinit cpu_init (void)
264 fpu_init(); 265 fpu_init();
265 266
266 raw_local_save_flags(kernel_eflags); 267 raw_local_save_flags(kernel_eflags);
268
269 if (is_uv_system())
270 uv_cpu_init();
267} 271}
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 5da35d2cdbd8..22bf6c29454f 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1101,6 +1101,7 @@ static __init void disable_smp(void)
1101 */ 1101 */
1102static int __init smp_sanity_check(unsigned max_cpus) 1102static int __init smp_sanity_check(unsigned max_cpus)
1103{ 1103{
1104 preempt_disable();
1104 if (!physid_isset(hard_smp_processor_id(), phys_cpu_present_map)) { 1105 if (!physid_isset(hard_smp_processor_id(), phys_cpu_present_map)) {
1105 printk(KERN_WARNING "weird, boot CPU (#%d) not listed" 1106 printk(KERN_WARNING "weird, boot CPU (#%d) not listed"
1106 "by the BIOS.\n", hard_smp_processor_id()); 1107 "by the BIOS.\n", hard_smp_processor_id());
@@ -1112,6 +1113,7 @@ static int __init smp_sanity_check(unsigned max_cpus)
1112 * get out of here now! 1113 * get out of here now!
1113 */ 1114 */
1114 if (!smp_found_config && !acpi_lapic) { 1115 if (!smp_found_config && !acpi_lapic) {
1116 preempt_enable();
1115 printk(KERN_NOTICE "SMP motherboard not detected.\n"); 1117 printk(KERN_NOTICE "SMP motherboard not detected.\n");
1116 disable_smp(); 1118 disable_smp();
1117 if (APIC_init_uniprocessor()) 1119 if (APIC_init_uniprocessor())
@@ -1130,6 +1132,7 @@ static int __init smp_sanity_check(unsigned max_cpus)
1130 boot_cpu_physical_apicid); 1132 boot_cpu_physical_apicid);
1131 physid_set(hard_smp_processor_id(), phys_cpu_present_map); 1133 physid_set(hard_smp_processor_id(), phys_cpu_present_map);
1132 } 1134 }
1135 preempt_enable();
1133 1136
1134 /* 1137 /*
1135 * If we couldn't find a local APIC, then get out of here now! 1138 * 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)
1205 return; 1208 return;
1206 } 1209 }
1207 1210
1211 preempt_disable();
1208 if (GET_APIC_ID(read_apic_id()) != boot_cpu_physical_apicid) { 1212 if (GET_APIC_ID(read_apic_id()) != boot_cpu_physical_apicid) {
1209 panic("Boot APIC ID in local APIC unexpected (%d vs %d)", 1213 panic("Boot APIC ID in local APIC unexpected (%d vs %d)",
1210 GET_APIC_ID(read_apic_id()), boot_cpu_physical_apicid); 1214 GET_APIC_ID(read_apic_id()), boot_cpu_physical_apicid);
1211 /* Or can we switch back to PIC here? */ 1215 /* Or can we switch back to PIC here? */
1212 } 1216 }
1217 preempt_enable();
1213 1218
1214#ifdef CONFIG_X86_32 1219#ifdef CONFIG_X86_32
1215 connect_bsp_APIC(); 1220 connect_bsp_APIC();