diff options
Diffstat (limited to 'arch/sparc64/kernel/smp.c')
-rw-r--r-- | arch/sparc64/kernel/smp.c | 251 |
1 files changed, 201 insertions, 50 deletions
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 40e40f968d61..b448d33321c6 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* smp.c: Sparc64 SMP support. | 1 | /* smp.c: Sparc64 SMP support. |
2 | * | 2 | * |
3 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) | 3 | * Copyright (C) 1997, 2007 David S. Miller (davem@davemloft.net) |
4 | */ | 4 | */ |
5 | 5 | ||
6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
@@ -28,6 +28,8 @@ | |||
28 | #include <asm/tlbflush.h> | 28 | #include <asm/tlbflush.h> |
29 | #include <asm/mmu_context.h> | 29 | #include <asm/mmu_context.h> |
30 | #include <asm/cpudata.h> | 30 | #include <asm/cpudata.h> |
31 | #include <asm/hvtramp.h> | ||
32 | #include <asm/io.h> | ||
31 | 33 | ||
32 | #include <asm/irq.h> | 34 | #include <asm/irq.h> |
33 | #include <asm/irq_regs.h> | 35 | #include <asm/irq_regs.h> |
@@ -41,22 +43,26 @@ | |||
41 | #include <asm/sections.h> | 43 | #include <asm/sections.h> |
42 | #include <asm/prom.h> | 44 | #include <asm/prom.h> |
43 | #include <asm/mdesc.h> | 45 | #include <asm/mdesc.h> |
46 | #include <asm/ldc.h> | ||
47 | #include <asm/hypervisor.h> | ||
44 | 48 | ||
45 | extern void calibrate_delay(void); | 49 | extern void calibrate_delay(void); |
46 | 50 | ||
47 | int sparc64_multi_core __read_mostly; | 51 | int sparc64_multi_core __read_mostly; |
48 | 52 | ||
49 | /* Please don't make this stuff initdata!!! --DaveM */ | 53 | cpumask_t cpu_possible_map __read_mostly = CPU_MASK_NONE; |
50 | unsigned char boot_cpu_id; | ||
51 | |||
52 | cpumask_t cpu_online_map __read_mostly = CPU_MASK_NONE; | 54 | cpumask_t cpu_online_map __read_mostly = CPU_MASK_NONE; |
53 | cpumask_t phys_cpu_present_map __read_mostly = CPU_MASK_NONE; | ||
54 | cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly = | 55 | cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly = |
55 | { [0 ... NR_CPUS-1] = CPU_MASK_NONE }; | 56 | { [0 ... NR_CPUS-1] = CPU_MASK_NONE }; |
56 | cpumask_t cpu_core_map[NR_CPUS] __read_mostly = | 57 | cpumask_t cpu_core_map[NR_CPUS] __read_mostly = |
57 | { [0 ... NR_CPUS-1] = CPU_MASK_NONE }; | 58 | { [0 ... NR_CPUS-1] = CPU_MASK_NONE }; |
59 | |||
60 | EXPORT_SYMBOL(cpu_possible_map); | ||
61 | EXPORT_SYMBOL(cpu_online_map); | ||
62 | EXPORT_SYMBOL(cpu_sibling_map); | ||
63 | EXPORT_SYMBOL(cpu_core_map); | ||
64 | |||
58 | static cpumask_t smp_commenced_mask; | 65 | static cpumask_t smp_commenced_mask; |
59 | static cpumask_t cpu_callout_map; | ||
60 | 66 | ||
61 | void smp_info(struct seq_file *m) | 67 | void smp_info(struct seq_file *m) |
62 | { | 68 | { |
@@ -73,18 +79,17 @@ void smp_bogo(struct seq_file *m) | |||
73 | 79 | ||
74 | for_each_online_cpu(i) | 80 | for_each_online_cpu(i) |
75 | seq_printf(m, | 81 | seq_printf(m, |
76 | "Cpu%dBogo\t: %lu.%02lu\n" | ||
77 | "Cpu%dClkTck\t: %016lx\n", | 82 | "Cpu%dClkTck\t: %016lx\n", |
78 | i, cpu_data(i).udelay_val / (500000/HZ), | ||
79 | (cpu_data(i).udelay_val / (5000/HZ)) % 100, | ||
80 | i, cpu_data(i).clock_tick); | 83 | i, cpu_data(i).clock_tick); |
81 | } | 84 | } |
82 | 85 | ||
86 | static __cacheline_aligned_in_smp DEFINE_SPINLOCK(call_lock); | ||
87 | |||
83 | extern void setup_sparc64_timer(void); | 88 | extern void setup_sparc64_timer(void); |
84 | 89 | ||
85 | static volatile unsigned long callin_flag = 0; | 90 | static volatile unsigned long callin_flag = 0; |
86 | 91 | ||
87 | void __init smp_callin(void) | 92 | void __devinit smp_callin(void) |
88 | { | 93 | { |
89 | int cpuid = hard_smp_processor_id(); | 94 | int cpuid = hard_smp_processor_id(); |
90 | 95 | ||
@@ -102,8 +107,6 @@ void __init smp_callin(void) | |||
102 | 107 | ||
103 | local_irq_enable(); | 108 | local_irq_enable(); |
104 | 109 | ||
105 | calibrate_delay(); | ||
106 | cpu_data(cpuid).udelay_val = loops_per_jiffy; | ||
107 | callin_flag = 1; | 110 | callin_flag = 1; |
108 | __asm__ __volatile__("membar #Sync\n\t" | 111 | __asm__ __volatile__("membar #Sync\n\t" |
109 | "flush %%g6" : : : "memory"); | 112 | "flush %%g6" : : : "memory"); |
@@ -120,7 +123,9 @@ void __init smp_callin(void) | |||
120 | while (!cpu_isset(cpuid, smp_commenced_mask)) | 123 | while (!cpu_isset(cpuid, smp_commenced_mask)) |
121 | rmb(); | 124 | rmb(); |
122 | 125 | ||
126 | spin_lock(&call_lock); | ||
123 | cpu_set(cpuid, cpu_online_map); | 127 | cpu_set(cpuid, cpu_online_map); |
128 | spin_unlock(&call_lock); | ||
124 | 129 | ||
125 | /* idle thread is expected to have preempt disabled */ | 130 | /* idle thread is expected to have preempt disabled */ |
126 | preempt_disable(); | 131 | preempt_disable(); |
@@ -268,6 +273,67 @@ static void smp_synchronize_one_tick(int cpu) | |||
268 | spin_unlock_irqrestore(&itc_sync_lock, flags); | 273 | spin_unlock_irqrestore(&itc_sync_lock, flags); |
269 | } | 274 | } |
270 | 275 | ||
276 | #if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU) | ||
277 | /* XXX Put this in some common place. XXX */ | ||
278 | static unsigned long kimage_addr_to_ra(void *p) | ||
279 | { | ||
280 | unsigned long val = (unsigned long) p; | ||
281 | |||
282 | return kern_base + (val - KERNBASE); | ||
283 | } | ||
284 | |||
285 | static void ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg) | ||
286 | { | ||
287 | extern unsigned long sparc64_ttable_tl0; | ||
288 | extern unsigned long kern_locked_tte_data; | ||
289 | extern int bigkernel; | ||
290 | struct hvtramp_descr *hdesc; | ||
291 | unsigned long trampoline_ra; | ||
292 | struct trap_per_cpu *tb; | ||
293 | u64 tte_vaddr, tte_data; | ||
294 | unsigned long hv_err; | ||
295 | |||
296 | hdesc = kzalloc(sizeof(*hdesc), GFP_KERNEL); | ||
297 | if (!hdesc) { | ||
298 | printk(KERN_ERR "ldom_startcpu_cpuid: Cannot allocate " | ||
299 | "hvtramp_descr.\n"); | ||
300 | return; | ||
301 | } | ||
302 | |||
303 | hdesc->cpu = cpu; | ||
304 | hdesc->num_mappings = (bigkernel ? 2 : 1); | ||
305 | |||
306 | tb = &trap_block[cpu]; | ||
307 | tb->hdesc = hdesc; | ||
308 | |||
309 | hdesc->fault_info_va = (unsigned long) &tb->fault_info; | ||
310 | hdesc->fault_info_pa = kimage_addr_to_ra(&tb->fault_info); | ||
311 | |||
312 | hdesc->thread_reg = thread_reg; | ||
313 | |||
314 | tte_vaddr = (unsigned long) KERNBASE; | ||
315 | tte_data = kern_locked_tte_data; | ||
316 | |||
317 | hdesc->maps[0].vaddr = tte_vaddr; | ||
318 | hdesc->maps[0].tte = tte_data; | ||
319 | if (bigkernel) { | ||
320 | tte_vaddr += 0x400000; | ||
321 | tte_data += 0x400000; | ||
322 | hdesc->maps[1].vaddr = tte_vaddr; | ||
323 | hdesc->maps[1].tte = tte_data; | ||
324 | } | ||
325 | |||
326 | trampoline_ra = kimage_addr_to_ra(hv_cpu_startup); | ||
327 | |||
328 | hv_err = sun4v_cpu_start(cpu, trampoline_ra, | ||
329 | kimage_addr_to_ra(&sparc64_ttable_tl0), | ||
330 | __pa(hdesc)); | ||
331 | if (hv_err) | ||
332 | printk(KERN_ERR "ldom_startcpu_cpuid: sun4v_cpu_start() " | ||
333 | "gives error %lu\n", hv_err); | ||
334 | } | ||
335 | #endif | ||
336 | |||
271 | extern void sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load); | 337 | extern void sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load); |
272 | 338 | ||
273 | extern unsigned long sparc64_cpu_startup; | 339 | extern unsigned long sparc64_cpu_startup; |
@@ -280,6 +346,7 @@ static struct thread_info *cpu_new_thread = NULL; | |||
280 | 346 | ||
281 | static int __devinit smp_boot_one_cpu(unsigned int cpu) | 347 | static int __devinit smp_boot_one_cpu(unsigned int cpu) |
282 | { | 348 | { |
349 | struct trap_per_cpu *tb = &trap_block[cpu]; | ||
283 | unsigned long entry = | 350 | unsigned long entry = |
284 | (unsigned long)(&sparc64_cpu_startup); | 351 | (unsigned long)(&sparc64_cpu_startup); |
285 | unsigned long cookie = | 352 | unsigned long cookie = |
@@ -290,20 +357,25 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu) | |||
290 | p = fork_idle(cpu); | 357 | p = fork_idle(cpu); |
291 | callin_flag = 0; | 358 | callin_flag = 0; |
292 | cpu_new_thread = task_thread_info(p); | 359 | cpu_new_thread = task_thread_info(p); |
293 | cpu_set(cpu, cpu_callout_map); | ||
294 | 360 | ||
295 | if (tlb_type == hypervisor) { | 361 | if (tlb_type == hypervisor) { |
296 | /* Alloc the mondo queues, cpu will load them. */ | 362 | /* Alloc the mondo queues, cpu will load them. */ |
297 | sun4v_init_mondo_queues(0, cpu, 1, 0); | 363 | sun4v_init_mondo_queues(0, cpu, 1, 0); |
298 | 364 | ||
299 | prom_startcpu_cpuid(cpu, entry, cookie); | 365 | #if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU) |
366 | if (ldom_domaining_enabled) | ||
367 | ldom_startcpu_cpuid(cpu, | ||
368 | (unsigned long) cpu_new_thread); | ||
369 | else | ||
370 | #endif | ||
371 | prom_startcpu_cpuid(cpu, entry, cookie); | ||
300 | } else { | 372 | } else { |
301 | struct device_node *dp = of_find_node_by_cpuid(cpu); | 373 | struct device_node *dp = of_find_node_by_cpuid(cpu); |
302 | 374 | ||
303 | prom_startcpu(dp->node, entry, cookie); | 375 | prom_startcpu(dp->node, entry, cookie); |
304 | } | 376 | } |
305 | 377 | ||
306 | for (timeout = 0; timeout < 5000000; timeout++) { | 378 | for (timeout = 0; timeout < 50000; timeout++) { |
307 | if (callin_flag) | 379 | if (callin_flag) |
308 | break; | 380 | break; |
309 | udelay(100); | 381 | udelay(100); |
@@ -313,11 +385,15 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu) | |||
313 | ret = 0; | 385 | ret = 0; |
314 | } else { | 386 | } else { |
315 | printk("Processor %d is stuck.\n", cpu); | 387 | printk("Processor %d is stuck.\n", cpu); |
316 | cpu_clear(cpu, cpu_callout_map); | ||
317 | ret = -ENODEV; | 388 | ret = -ENODEV; |
318 | } | 389 | } |
319 | cpu_new_thread = NULL; | 390 | cpu_new_thread = NULL; |
320 | 391 | ||
392 | if (tb->hdesc) { | ||
393 | kfree(tb->hdesc); | ||
394 | tb->hdesc = NULL; | ||
395 | } | ||
396 | |||
321 | return ret; | 397 | return ret; |
322 | } | 398 | } |
323 | 399 | ||
@@ -720,7 +796,6 @@ struct call_data_struct { | |||
720 | int wait; | 796 | int wait; |
721 | }; | 797 | }; |
722 | 798 | ||
723 | static __cacheline_aligned_in_smp DEFINE_SPINLOCK(call_lock); | ||
724 | static struct call_data_struct *call_data; | 799 | static struct call_data_struct *call_data; |
725 | 800 | ||
726 | extern unsigned long xcall_call_function; | 801 | extern unsigned long xcall_call_function; |
@@ -1152,34 +1227,14 @@ void smp_penguin_jailcell(int irq, struct pt_regs *regs) | |||
1152 | preempt_enable(); | 1227 | preempt_enable(); |
1153 | } | 1228 | } |
1154 | 1229 | ||
1155 | void __init smp_tick_init(void) | ||
1156 | { | ||
1157 | boot_cpu_id = hard_smp_processor_id(); | ||
1158 | } | ||
1159 | |||
1160 | /* /proc/profile writes can call this, don't __init it please. */ | 1230 | /* /proc/profile writes can call this, don't __init it please. */ |
1161 | int setup_profiling_timer(unsigned int multiplier) | 1231 | int setup_profiling_timer(unsigned int multiplier) |
1162 | { | 1232 | { |
1163 | return -EINVAL; | 1233 | return -EINVAL; |
1164 | } | 1234 | } |
1165 | 1235 | ||
1166 | /* Constrain the number of cpus to max_cpus. */ | ||
1167 | void __init smp_prepare_cpus(unsigned int max_cpus) | 1236 | void __init smp_prepare_cpus(unsigned int max_cpus) |
1168 | { | 1237 | { |
1169 | int i; | ||
1170 | |||
1171 | if (num_possible_cpus() > max_cpus) { | ||
1172 | for_each_possible_cpu(i) { | ||
1173 | if (i != boot_cpu_id) { | ||
1174 | cpu_clear(i, phys_cpu_present_map); | ||
1175 | cpu_clear(i, cpu_present_map); | ||
1176 | if (num_possible_cpus() <= max_cpus) | ||
1177 | break; | ||
1178 | } | ||
1179 | } | ||
1180 | } | ||
1181 | |||
1182 | cpu_data(boot_cpu_id).udelay_val = loops_per_jiffy; | ||
1183 | } | 1238 | } |
1184 | 1239 | ||
1185 | void __devinit smp_prepare_boot_cpu(void) | 1240 | void __devinit smp_prepare_boot_cpu(void) |
@@ -1190,30 +1245,32 @@ void __devinit smp_fill_in_sib_core_maps(void) | |||
1190 | { | 1245 | { |
1191 | unsigned int i; | 1246 | unsigned int i; |
1192 | 1247 | ||
1193 | for_each_possible_cpu(i) { | 1248 | for_each_present_cpu(i) { |
1194 | unsigned int j; | 1249 | unsigned int j; |
1195 | 1250 | ||
1251 | cpus_clear(cpu_core_map[i]); | ||
1196 | if (cpu_data(i).core_id == 0) { | 1252 | if (cpu_data(i).core_id == 0) { |
1197 | cpu_set(i, cpu_core_map[i]); | 1253 | cpu_set(i, cpu_core_map[i]); |
1198 | continue; | 1254 | continue; |
1199 | } | 1255 | } |
1200 | 1256 | ||
1201 | for_each_possible_cpu(j) { | 1257 | for_each_present_cpu(j) { |
1202 | if (cpu_data(i).core_id == | 1258 | if (cpu_data(i).core_id == |
1203 | cpu_data(j).core_id) | 1259 | cpu_data(j).core_id) |
1204 | cpu_set(j, cpu_core_map[i]); | 1260 | cpu_set(j, cpu_core_map[i]); |
1205 | } | 1261 | } |
1206 | } | 1262 | } |
1207 | 1263 | ||
1208 | for_each_possible_cpu(i) { | 1264 | for_each_present_cpu(i) { |
1209 | unsigned int j; | 1265 | unsigned int j; |
1210 | 1266 | ||
1267 | cpus_clear(cpu_sibling_map[i]); | ||
1211 | if (cpu_data(i).proc_id == -1) { | 1268 | if (cpu_data(i).proc_id == -1) { |
1212 | cpu_set(i, cpu_sibling_map[i]); | 1269 | cpu_set(i, cpu_sibling_map[i]); |
1213 | continue; | 1270 | continue; |
1214 | } | 1271 | } |
1215 | 1272 | ||
1216 | for_each_possible_cpu(j) { | 1273 | for_each_present_cpu(j) { |
1217 | if (cpu_data(i).proc_id == | 1274 | if (cpu_data(i).proc_id == |
1218 | cpu_data(j).proc_id) | 1275 | cpu_data(j).proc_id) |
1219 | cpu_set(j, cpu_sibling_map[i]); | 1276 | cpu_set(j, cpu_sibling_map[i]); |
@@ -1242,18 +1299,112 @@ int __cpuinit __cpu_up(unsigned int cpu) | |||
1242 | return ret; | 1299 | return ret; |
1243 | } | 1300 | } |
1244 | 1301 | ||
1245 | void __init smp_cpus_done(unsigned int max_cpus) | 1302 | #ifdef CONFIG_HOTPLUG_CPU |
1303 | void cpu_play_dead(void) | ||
1304 | { | ||
1305 | int cpu = smp_processor_id(); | ||
1306 | unsigned long pstate; | ||
1307 | |||
1308 | idle_task_exit(); | ||
1309 | |||
1310 | if (tlb_type == hypervisor) { | ||
1311 | struct trap_per_cpu *tb = &trap_block[cpu]; | ||
1312 | |||
1313 | sun4v_cpu_qconf(HV_CPU_QUEUE_CPU_MONDO, | ||
1314 | tb->cpu_mondo_pa, 0); | ||
1315 | sun4v_cpu_qconf(HV_CPU_QUEUE_DEVICE_MONDO, | ||
1316 | tb->dev_mondo_pa, 0); | ||
1317 | sun4v_cpu_qconf(HV_CPU_QUEUE_RES_ERROR, | ||
1318 | tb->resum_mondo_pa, 0); | ||
1319 | sun4v_cpu_qconf(HV_CPU_QUEUE_NONRES_ERROR, | ||
1320 | tb->nonresum_mondo_pa, 0); | ||
1321 | } | ||
1322 | |||
1323 | cpu_clear(cpu, smp_commenced_mask); | ||
1324 | membar_safe("#Sync"); | ||
1325 | |||
1326 | local_irq_disable(); | ||
1327 | |||
1328 | __asm__ __volatile__( | ||
1329 | "rdpr %%pstate, %0\n\t" | ||
1330 | "wrpr %0, %1, %%pstate" | ||
1331 | : "=r" (pstate) | ||
1332 | : "i" (PSTATE_IE)); | ||
1333 | |||
1334 | while (1) | ||
1335 | barrier(); | ||
1336 | } | ||
1337 | |||
1338 | int __cpu_disable(void) | ||
1246 | { | 1339 | { |
1247 | unsigned long bogosum = 0; | 1340 | int cpu = smp_processor_id(); |
1341 | cpuinfo_sparc *c; | ||
1248 | int i; | 1342 | int i; |
1249 | 1343 | ||
1250 | for_each_online_cpu(i) | 1344 | for_each_cpu_mask(i, cpu_core_map[cpu]) |
1251 | bogosum += cpu_data(i).udelay_val; | 1345 | cpu_clear(cpu, cpu_core_map[i]); |
1252 | printk("Total of %ld processors activated " | 1346 | cpus_clear(cpu_core_map[cpu]); |
1253 | "(%lu.%02lu BogoMIPS).\n", | 1347 | |
1254 | (long) num_online_cpus(), | 1348 | for_each_cpu_mask(i, cpu_sibling_map[cpu]) |
1255 | bogosum/(500000/HZ), | 1349 | cpu_clear(cpu, cpu_sibling_map[i]); |
1256 | (bogosum/(5000/HZ))%100); | 1350 | cpus_clear(cpu_sibling_map[cpu]); |
1351 | |||
1352 | c = &cpu_data(cpu); | ||
1353 | |||
1354 | c->core_id = 0; | ||
1355 | c->proc_id = -1; | ||
1356 | |||
1357 | spin_lock(&call_lock); | ||
1358 | cpu_clear(cpu, cpu_online_map); | ||
1359 | spin_unlock(&call_lock); | ||
1360 | |||
1361 | smp_wmb(); | ||
1362 | |||
1363 | /* Make sure no interrupts point to this cpu. */ | ||
1364 | fixup_irqs(); | ||
1365 | |||
1366 | local_irq_enable(); | ||
1367 | mdelay(1); | ||
1368 | local_irq_disable(); | ||
1369 | |||
1370 | return 0; | ||
1371 | } | ||
1372 | |||
1373 | void __cpu_die(unsigned int cpu) | ||
1374 | { | ||
1375 | int i; | ||
1376 | |||
1377 | for (i = 0; i < 100; i++) { | ||
1378 | smp_rmb(); | ||
1379 | if (!cpu_isset(cpu, smp_commenced_mask)) | ||
1380 | break; | ||
1381 | msleep(100); | ||
1382 | } | ||
1383 | if (cpu_isset(cpu, smp_commenced_mask)) { | ||
1384 | printk(KERN_ERR "CPU %u didn't die...\n", cpu); | ||
1385 | } else { | ||
1386 | #if defined(CONFIG_SUN_LDOMS) | ||
1387 | unsigned long hv_err; | ||
1388 | int limit = 100; | ||
1389 | |||
1390 | do { | ||
1391 | hv_err = sun4v_cpu_stop(cpu); | ||
1392 | if (hv_err == HV_EOK) { | ||
1393 | cpu_clear(cpu, cpu_present_map); | ||
1394 | break; | ||
1395 | } | ||
1396 | } while (--limit > 0); | ||
1397 | if (limit <= 0) { | ||
1398 | printk(KERN_ERR "sun4v_cpu_stop() fails err=%lu\n", | ||
1399 | hv_err); | ||
1400 | } | ||
1401 | #endif | ||
1402 | } | ||
1403 | } | ||
1404 | #endif | ||
1405 | |||
1406 | void __init smp_cpus_done(unsigned int max_cpus) | ||
1407 | { | ||
1257 | } | 1408 | } |
1258 | 1409 | ||
1259 | void smp_send_reschedule(int cpu) | 1410 | void smp_send_reschedule(int cpu) |