| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * SMP support for ppc. | 
|  | 3 | * | 
|  | 4 | * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great | 
|  | 5 | * deal of code from the sparc and intel versions. | 
|  | 6 | * | 
|  | 7 | * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu> | 
|  | 8 | * | 
|  | 9 | * PowerPC-64 Support added by Dave Engebretsen, Peter Bergner, and | 
|  | 10 | * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com | 
|  | 11 | * | 
|  | 12 | *      This program is free software; you can redistribute it and/or | 
|  | 13 | *      modify it under the terms of the GNU General Public License | 
|  | 14 | *      as published by the Free Software Foundation; either version | 
|  | 15 | *      2 of the License, or (at your option) any later version. | 
|  | 16 | */ | 
|  | 17 |  | 
|  | 18 | #undef DEBUG | 
|  | 19 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 20 | #include <linux/kernel.h> | 
|  | 21 | #include <linux/module.h> | 
|  | 22 | #include <linux/sched.h> | 
|  | 23 | #include <linux/smp.h> | 
|  | 24 | #include <linux/interrupt.h> | 
|  | 25 | #include <linux/delay.h> | 
|  | 26 | #include <linux/init.h> | 
|  | 27 | #include <linux/spinlock.h> | 
|  | 28 | #include <linux/cache.h> | 
|  | 29 | #include <linux/err.h> | 
|  | 30 | #include <linux/sysdev.h> | 
|  | 31 | #include <linux/cpu.h> | 
|  | 32 | #include <linux/notifier.h> | 
| Anton Blanchard | 4b703a2 | 2005-12-13 06:56:47 +1100 | [diff] [blame] | 33 | #include <linux/topology.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 34 |  | 
|  | 35 | #include <asm/ptrace.h> | 
|  | 36 | #include <asm/atomic.h> | 
|  | 37 | #include <asm/irq.h> | 
|  | 38 | #include <asm/page.h> | 
|  | 39 | #include <asm/pgtable.h> | 
|  | 40 | #include <asm/prom.h> | 
|  | 41 | #include <asm/smp.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 42 | #include <asm/time.h> | 
|  | 43 | #include <asm/machdep.h> | 
| Nathan Lynch | e2075f7 | 2008-07-27 15:24:52 +1000 | [diff] [blame] | 44 | #include <asm/cputhreads.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | #include <asm/cputable.h> | 
|  | 46 | #include <asm/system.h> | 
| Stephen Rothwell | bbeb3f4 | 2005-09-27 13:51:59 +1000 | [diff] [blame] | 47 | #include <asm/mpic.h> | 
| Benjamin Herrenschmidt | a7f290d | 2005-11-11 21:15:21 +1100 | [diff] [blame] | 48 | #include <asm/vdso_datapage.h> | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 49 | #ifdef CONFIG_PPC64 | 
|  | 50 | #include <asm/paca.h> | 
|  | 51 | #endif | 
|  | 52 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 | #ifdef DEBUG | 
| Michael Ellerman | f9e4ec5 | 2005-11-15 15:16:38 +1100 | [diff] [blame] | 54 | #include <asm/udbg.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 | #define DBG(fmt...) udbg_printf(fmt) | 
|  | 56 | #else | 
|  | 57 | #define DBG(fmt...) | 
|  | 58 | #endif | 
|  | 59 |  | 
| Michael Ellerman | f9e4ec5 | 2005-11-15 15:16:38 +1100 | [diff] [blame] | 60 | int smp_hw_index[NR_CPUS]; | 
|  | 61 | struct thread_info *secondary_ti; | 
|  | 62 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 63 | cpumask_t cpu_possible_map = CPU_MASK_NONE; | 
|  | 64 | cpumask_t cpu_online_map = CPU_MASK_NONE; | 
| Mike Travis | d5a7430 | 2007-10-16 01:24:05 -0700 | [diff] [blame] | 65 | DEFINE_PER_CPU(cpumask_t, cpu_sibling_map) = CPU_MASK_NONE; | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 66 | DEFINE_PER_CPU(cpumask_t, cpu_core_map) = CPU_MASK_NONE; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 |  | 
|  | 68 | EXPORT_SYMBOL(cpu_online_map); | 
|  | 69 | EXPORT_SYMBOL(cpu_possible_map); | 
| Mike Travis | d5a7430 | 2007-10-16 01:24:05 -0700 | [diff] [blame] | 70 | EXPORT_PER_CPU_SYMBOL(cpu_sibling_map); | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 71 | EXPORT_PER_CPU_SYMBOL(cpu_core_map); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 72 |  | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 73 | /* SMP operations for this machine */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 74 | struct smp_ops_t *smp_ops; | 
|  | 75 |  | 
|  | 76 | static volatile unsigned int cpu_callin_map[NR_CPUS]; | 
|  | 77 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | int smt_enabled_at_boot = 1; | 
|  | 79 |  | 
| Michael Ellerman | cc53291 | 2005-12-04 18:39:43 +1100 | [diff] [blame] | 80 | static void (*crash_ipi_function_ptr)(struct pt_regs *) = NULL; | 
|  | 81 |  | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 82 | #ifdef CONFIG_PPC64 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | void __devinit smp_generic_kick_cpu(int nr) | 
|  | 84 | { | 
|  | 85 | BUG_ON(nr < 0 || nr >= NR_CPUS); | 
|  | 86 |  | 
|  | 87 | /* | 
|  | 88 | * The processor is currently spinning, waiting for the | 
|  | 89 | * cpu_start field to become non-zero After we set cpu_start, | 
|  | 90 | * the processor will continue on to secondary_start | 
|  | 91 | */ | 
|  | 92 | paca[nr].cpu_start = 1; | 
| Anton Blanchard | 0d8d4d4 | 2005-05-01 08:58:47 -0700 | [diff] [blame] | 93 | smp_mb(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 94 | } | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 95 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 97 | void smp_message_recv(int msg) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 98 | { | 
|  | 99 | switch(msg) { | 
|  | 100 | case PPC_MSG_CALL_FUNCTION: | 
| Jens Axboe | b7d7a24 | 2008-06-26 11:22:13 +0200 | [diff] [blame] | 101 | generic_smp_call_function_interrupt(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 102 | break; | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 103 | case PPC_MSG_RESCHEDULE: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 104 | /* XXX Do we have to do this? */ | 
|  | 105 | set_need_resched(); | 
|  | 106 | break; | 
| Jens Axboe | b7d7a24 | 2008-06-26 11:22:13 +0200 | [diff] [blame] | 107 | case PPC_MSG_CALL_FUNC_SINGLE: | 
|  | 108 | generic_smp_call_function_single_interrupt(); | 
|  | 109 | break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 110 | case PPC_MSG_DEBUGGER_BREAK: | 
| Michael Ellerman | cc53291 | 2005-12-04 18:39:43 +1100 | [diff] [blame] | 111 | if (crash_ipi_function_ptr) { | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 112 | crash_ipi_function_ptr(get_irq_regs()); | 
| Michael Ellerman | cc53291 | 2005-12-04 18:39:43 +1100 | [diff] [blame] | 113 | break; | 
|  | 114 | } | 
|  | 115 | #ifdef CONFIG_DEBUGGER | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 116 | debugger_ipi(get_irq_regs()); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | break; | 
| Michael Ellerman | cc53291 | 2005-12-04 18:39:43 +1100 | [diff] [blame] | 118 | #endif /* CONFIG_DEBUGGER */ | 
|  | 119 | /* FALLTHROUGH */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 120 | default: | 
|  | 121 | printk("SMP %d: smp_message_recv(): unknown msg %d\n", | 
|  | 122 | smp_processor_id(), msg); | 
|  | 123 | break; | 
|  | 124 | } | 
|  | 125 | } | 
|  | 126 |  | 
|  | 127 | void smp_send_reschedule(int cpu) | 
|  | 128 | { | 
| Benjamin Herrenschmidt | 8cffc6a | 2006-07-04 14:09:36 +1000 | [diff] [blame] | 129 | if (likely(smp_ops)) | 
|  | 130 | smp_ops->message_pass(cpu, PPC_MSG_RESCHEDULE); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 131 | } | 
|  | 132 |  | 
| Jens Axboe | b7d7a24 | 2008-06-26 11:22:13 +0200 | [diff] [blame] | 133 | void arch_send_call_function_single_ipi(int cpu) | 
|  | 134 | { | 
|  | 135 | smp_ops->message_pass(cpu, PPC_MSG_CALL_FUNC_SINGLE); | 
|  | 136 | } | 
|  | 137 |  | 
|  | 138 | void arch_send_call_function_ipi(cpumask_t mask) | 
|  | 139 | { | 
|  | 140 | unsigned int cpu; | 
|  | 141 |  | 
|  | 142 | for_each_cpu_mask(cpu, mask) | 
|  | 143 | smp_ops->message_pass(cpu, PPC_MSG_CALL_FUNCTION); | 
|  | 144 | } | 
|  | 145 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 146 | #ifdef CONFIG_DEBUGGER | 
|  | 147 | void smp_send_debugger_break(int cpu) | 
|  | 148 | { | 
| Benjamin Herrenschmidt | 8cffc6a | 2006-07-04 14:09:36 +1000 | [diff] [blame] | 149 | if (likely(smp_ops)) | 
|  | 150 | smp_ops->message_pass(cpu, PPC_MSG_DEBUGGER_BREAK); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 151 | } | 
|  | 152 | #endif | 
|  | 153 |  | 
| Michael Ellerman | cc53291 | 2005-12-04 18:39:43 +1100 | [diff] [blame] | 154 | #ifdef CONFIG_KEXEC | 
|  | 155 | void crash_send_ipi(void (*crash_ipi_callback)(struct pt_regs *)) | 
|  | 156 | { | 
|  | 157 | crash_ipi_function_ptr = crash_ipi_callback; | 
| Benjamin Herrenschmidt | 8cffc6a | 2006-07-04 14:09:36 +1000 | [diff] [blame] | 158 | if (crash_ipi_callback && smp_ops) { | 
| Michael Ellerman | cc53291 | 2005-12-04 18:39:43 +1100 | [diff] [blame] | 159 | mb(); | 
|  | 160 | smp_ops->message_pass(MSG_ALL_BUT_SELF, PPC_MSG_DEBUGGER_BREAK); | 
|  | 161 | } | 
|  | 162 | } | 
|  | 163 | #endif | 
|  | 164 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 165 | static void stop_this_cpu(void *dummy) | 
|  | 166 | { | 
|  | 167 | local_irq_disable(); | 
|  | 168 | while (1) | 
|  | 169 | ; | 
|  | 170 | } | 
|  | 171 |  | 
| Satyam Sharma | 8fd7675 | 2007-09-18 09:43:40 +1000 | [diff] [blame] | 172 | void smp_send_stop(void) | 
|  | 173 | { | 
| Jens Axboe | 8691e5a | 2008-06-06 11:18:06 +0200 | [diff] [blame] | 174 | smp_call_function(stop_this_cpu, NULL, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 175 | } | 
|  | 176 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 177 | struct thread_info *current_set[NR_CPUS]; | 
|  | 178 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 179 | static void __devinit smp_store_cpu_info(int id) | 
|  | 180 | { | 
|  | 181 | per_cpu(pvr, id) = mfspr(SPRN_PVR); | 
|  | 182 | } | 
|  | 183 |  | 
|  | 184 | static void __init smp_create_idle(unsigned int cpu) | 
|  | 185 | { | 
|  | 186 | struct task_struct *p; | 
|  | 187 |  | 
|  | 188 | /* create a process for the processor */ | 
|  | 189 | p = fork_idle(cpu); | 
|  | 190 | if (IS_ERR(p)) | 
|  | 191 | panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p)); | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 192 | #ifdef CONFIG_PPC64 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 193 | paca[cpu].__current = p; | 
| Paul Mackerras | 3b57506 | 2008-05-02 14:29:12 +1000 | [diff] [blame] | 194 | paca[cpu].kstack = (unsigned long) task_thread_info(p) | 
|  | 195 | + THREAD_SIZE - STACK_FRAME_OVERHEAD; | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 196 | #endif | 
| Al Viro | b5e2fc1 | 2006-01-12 01:06:01 -0800 | [diff] [blame] | 197 | current_set[cpu] = task_thread_info(p); | 
|  | 198 | task_thread_info(p)->cpu = cpu; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 199 | } | 
|  | 200 |  | 
|  | 201 | void __init smp_prepare_cpus(unsigned int max_cpus) | 
|  | 202 | { | 
|  | 203 | unsigned int cpu; | 
|  | 204 |  | 
|  | 205 | DBG("smp_prepare_cpus\n"); | 
|  | 206 |  | 
|  | 207 | /* | 
|  | 208 | * setup_cpu may need to be called on the boot cpu. We havent | 
|  | 209 | * spun any cpus up but lets be paranoid. | 
|  | 210 | */ | 
|  | 211 | BUG_ON(boot_cpuid != smp_processor_id()); | 
|  | 212 |  | 
|  | 213 | /* Fixup boot cpu */ | 
|  | 214 | smp_store_cpu_info(boot_cpuid); | 
|  | 215 | cpu_callin_map[boot_cpuid] = 1; | 
|  | 216 |  | 
| Benjamin Herrenschmidt | 8cffc6a | 2006-07-04 14:09:36 +1000 | [diff] [blame] | 217 | if (smp_ops) | 
|  | 218 | max_cpus = smp_ops->probe(); | 
|  | 219 | else | 
|  | 220 | max_cpus = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 221 |  | 
|  | 222 | smp_space_timers(max_cpus); | 
|  | 223 |  | 
| KAMEZAWA Hiroyuki | 0e55195 | 2006-03-28 14:50:51 -0800 | [diff] [blame] | 224 | for_each_possible_cpu(cpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 225 | if (cpu != boot_cpuid) | 
|  | 226 | smp_create_idle(cpu); | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | void __devinit smp_prepare_boot_cpu(void) | 
|  | 230 | { | 
|  | 231 | BUG_ON(smp_processor_id() != boot_cpuid); | 
|  | 232 |  | 
|  | 233 | cpu_set(boot_cpuid, cpu_online_map); | 
| Nathan Lynch | e2075f7 | 2008-07-27 15:24:52 +1000 | [diff] [blame] | 234 | cpu_set(boot_cpuid, per_cpu(cpu_sibling_map, boot_cpuid)); | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 235 | cpu_set(boot_cpuid, per_cpu(cpu_core_map, boot_cpuid)); | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 236 | #ifdef CONFIG_PPC64 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 237 | paca[boot_cpuid].__current = current; | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 238 | #endif | 
| Al Viro | b5e2fc1 | 2006-01-12 01:06:01 -0800 | [diff] [blame] | 239 | current_set[boot_cpuid] = task_thread_info(current); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 240 | } | 
|  | 241 |  | 
|  | 242 | #ifdef CONFIG_HOTPLUG_CPU | 
|  | 243 | /* State of each CPU during hotplug phases */ | 
|  | 244 | DEFINE_PER_CPU(int, cpu_state) = { 0 }; | 
|  | 245 |  | 
|  | 246 | int generic_cpu_disable(void) | 
|  | 247 | { | 
|  | 248 | unsigned int cpu = smp_processor_id(); | 
|  | 249 |  | 
|  | 250 | if (cpu == boot_cpuid) | 
|  | 251 | return -EBUSY; | 
|  | 252 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 253 | cpu_clear(cpu, cpu_online_map); | 
| Paul Mackerras | 799d604 | 2005-11-10 13:37:51 +1100 | [diff] [blame] | 254 | #ifdef CONFIG_PPC64 | 
| Benjamin Herrenschmidt | a7f290d | 2005-11-11 21:15:21 +1100 | [diff] [blame] | 255 | vdso_data->processorCount--; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 256 | fixup_irqs(cpu_online_map); | 
| Paul Mackerras | 094fe2e | 2005-11-10 14:26:12 +1100 | [diff] [blame] | 257 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 258 | return 0; | 
|  | 259 | } | 
|  | 260 |  | 
|  | 261 | int generic_cpu_enable(unsigned int cpu) | 
|  | 262 | { | 
|  | 263 | /* Do the normal bootup if we haven't | 
|  | 264 | * already bootstrapped. */ | 
|  | 265 | if (system_state != SYSTEM_RUNNING) | 
|  | 266 | return -ENOSYS; | 
|  | 267 |  | 
|  | 268 | /* get the target out of it's holding state */ | 
|  | 269 | per_cpu(cpu_state, cpu) = CPU_UP_PREPARE; | 
| Anton Blanchard | 0d8d4d4 | 2005-05-01 08:58:47 -0700 | [diff] [blame] | 270 | smp_wmb(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 271 |  | 
|  | 272 | while (!cpu_online(cpu)) | 
|  | 273 | cpu_relax(); | 
|  | 274 |  | 
| Paul Mackerras | 094fe2e | 2005-11-10 14:26:12 +1100 | [diff] [blame] | 275 | #ifdef CONFIG_PPC64 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 276 | fixup_irqs(cpu_online_map); | 
|  | 277 | /* counter the irq disable in fixup_irqs */ | 
|  | 278 | local_irq_enable(); | 
| Paul Mackerras | 094fe2e | 2005-11-10 14:26:12 +1100 | [diff] [blame] | 279 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 280 | return 0; | 
|  | 281 | } | 
|  | 282 |  | 
|  | 283 | void generic_cpu_die(unsigned int cpu) | 
|  | 284 | { | 
|  | 285 | int i; | 
|  | 286 |  | 
|  | 287 | for (i = 0; i < 100; i++) { | 
| Anton Blanchard | 0d8d4d4 | 2005-05-01 08:58:47 -0700 | [diff] [blame] | 288 | smp_rmb(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 289 | if (per_cpu(cpu_state, cpu) == CPU_DEAD) | 
|  | 290 | return; | 
|  | 291 | msleep(100); | 
|  | 292 | } | 
|  | 293 | printk(KERN_ERR "CPU%d didn't die...\n", cpu); | 
|  | 294 | } | 
|  | 295 |  | 
|  | 296 | void generic_mach_cpu_die(void) | 
|  | 297 | { | 
|  | 298 | unsigned int cpu; | 
|  | 299 |  | 
|  | 300 | local_irq_disable(); | 
|  | 301 | cpu = smp_processor_id(); | 
|  | 302 | printk(KERN_DEBUG "CPU%d offline\n", cpu); | 
|  | 303 | __get_cpu_var(cpu_state) = CPU_DEAD; | 
| Anton Blanchard | 0d8d4d4 | 2005-05-01 08:58:47 -0700 | [diff] [blame] | 304 | smp_wmb(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 305 | while (__get_cpu_var(cpu_state) != CPU_UP_PREPARE) | 
|  | 306 | cpu_relax(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 307 | cpu_set(cpu, cpu_online_map); | 
|  | 308 | local_irq_enable(); | 
|  | 309 | } | 
|  | 310 | #endif | 
|  | 311 |  | 
|  | 312 | static int __devinit cpu_enable(unsigned int cpu) | 
|  | 313 | { | 
| Benjamin Herrenschmidt | 8cffc6a | 2006-07-04 14:09:36 +1000 | [diff] [blame] | 314 | if (smp_ops && smp_ops->cpu_enable) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 315 | return smp_ops->cpu_enable(cpu); | 
|  | 316 |  | 
|  | 317 | return -ENOSYS; | 
|  | 318 | } | 
|  | 319 |  | 
| Gautham R Shenoy | b282b6f | 2007-01-10 23:15:34 -0800 | [diff] [blame] | 320 | int __cpuinit __cpu_up(unsigned int cpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 321 | { | 
|  | 322 | int c; | 
|  | 323 |  | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 324 | secondary_ti = current_set[cpu]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 325 | if (!cpu_enable(cpu)) | 
|  | 326 | return 0; | 
|  | 327 |  | 
| Benjamin Herrenschmidt | 8cffc6a | 2006-07-04 14:09:36 +1000 | [diff] [blame] | 328 | if (smp_ops == NULL || | 
|  | 329 | (smp_ops->cpu_bootable && !smp_ops->cpu_bootable(cpu))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 330 | return -EINVAL; | 
|  | 331 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | /* Make sure callin-map entry is 0 (can be leftover a CPU | 
|  | 333 | * hotplug | 
|  | 334 | */ | 
|  | 335 | cpu_callin_map[cpu] = 0; | 
|  | 336 |  | 
|  | 337 | /* The information for processor bringup must | 
|  | 338 | * be written out to main store before we release | 
|  | 339 | * the processor. | 
|  | 340 | */ | 
| Anton Blanchard | 0d8d4d4 | 2005-05-01 08:58:47 -0700 | [diff] [blame] | 341 | smp_mb(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 342 |  | 
|  | 343 | /* wake up cpus */ | 
|  | 344 | DBG("smp: kicking cpu %d\n", cpu); | 
|  | 345 | smp_ops->kick_cpu(cpu); | 
|  | 346 |  | 
|  | 347 | /* | 
|  | 348 | * wait to see if the cpu made a callin (is actually up). | 
|  | 349 | * use this value that I found through experimentation. | 
|  | 350 | * -- Cort | 
|  | 351 | */ | 
|  | 352 | if (system_state < SYSTEM_RUNNING) | 
| Jon Loeliger | ee0339f | 2006-06-17 17:52:44 -0500 | [diff] [blame] | 353 | for (c = 50000; c && !cpu_callin_map[cpu]; c--) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 354 | udelay(100); | 
|  | 355 | #ifdef CONFIG_HOTPLUG_CPU | 
|  | 356 | else | 
|  | 357 | /* | 
|  | 358 | * CPUs can take much longer to come up in the | 
|  | 359 | * hotplug case.  Wait five seconds. | 
|  | 360 | */ | 
|  | 361 | for (c = 25; c && !cpu_callin_map[cpu]; c--) { | 
|  | 362 | msleep(200); | 
|  | 363 | } | 
|  | 364 | #endif | 
|  | 365 |  | 
|  | 366 | if (!cpu_callin_map[cpu]) { | 
|  | 367 | printk("Processor %u is stuck.\n", cpu); | 
|  | 368 | return -ENOENT; | 
|  | 369 | } | 
|  | 370 |  | 
|  | 371 | printk("Processor %u found.\n", cpu); | 
|  | 372 |  | 
|  | 373 | if (smp_ops->give_timebase) | 
|  | 374 | smp_ops->give_timebase(); | 
|  | 375 |  | 
|  | 376 | /* Wait until cpu puts itself in the online map */ | 
|  | 377 | while (!cpu_online(cpu)) | 
|  | 378 | cpu_relax(); | 
|  | 379 |  | 
|  | 380 | return 0; | 
|  | 381 | } | 
|  | 382 |  | 
| Nathan Lynch | e9efed3 | 2008-07-27 15:24:54 +1000 | [diff] [blame] | 383 | /* Return the value of the reg property corresponding to the given | 
|  | 384 | * logical cpu. | 
|  | 385 | */ | 
|  | 386 | int cpu_to_core_id(int cpu) | 
|  | 387 | { | 
|  | 388 | struct device_node *np; | 
|  | 389 | const int *reg; | 
|  | 390 | int id = -1; | 
|  | 391 |  | 
|  | 392 | np = of_get_cpu_node(cpu, NULL); | 
|  | 393 | if (!np) | 
|  | 394 | goto out; | 
|  | 395 |  | 
|  | 396 | reg = of_get_property(np, "reg", NULL); | 
|  | 397 | if (!reg) | 
|  | 398 | goto out; | 
|  | 399 |  | 
|  | 400 | id = *reg; | 
|  | 401 | out: | 
|  | 402 | of_node_put(np); | 
|  | 403 | return id; | 
|  | 404 | } | 
|  | 405 |  | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 406 | /* Must be called when no change can occur to cpu_present_map, | 
|  | 407 | * i.e. during cpu online or offline. | 
|  | 408 | */ | 
|  | 409 | static struct device_node *cpu_to_l2cache(int cpu) | 
|  | 410 | { | 
|  | 411 | struct device_node *np; | 
|  | 412 | const phandle *php; | 
|  | 413 | phandle ph; | 
|  | 414 |  | 
|  | 415 | if (!cpu_present(cpu)) | 
|  | 416 | return NULL; | 
|  | 417 |  | 
|  | 418 | np = of_get_cpu_node(cpu, NULL); | 
|  | 419 | if (np == NULL) | 
|  | 420 | return NULL; | 
|  | 421 |  | 
|  | 422 | php = of_get_property(np, "l2-cache", NULL); | 
|  | 423 | if (php == NULL) | 
|  | 424 | return NULL; | 
|  | 425 | ph = *php; | 
|  | 426 | of_node_put(np); | 
|  | 427 |  | 
|  | 428 | return of_find_node_by_phandle(ph); | 
|  | 429 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 430 |  | 
|  | 431 | /* Activate a secondary processor. */ | 
|  | 432 | int __devinit start_secondary(void *unused) | 
|  | 433 | { | 
|  | 434 | unsigned int cpu = smp_processor_id(); | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 435 | struct device_node *l2_cache; | 
| Nathan Lynch | e2075f7 | 2008-07-27 15:24:52 +1000 | [diff] [blame] | 436 | int i, base; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 437 |  | 
|  | 438 | atomic_inc(&init_mm.mm_count); | 
|  | 439 | current->active_mm = &init_mm; | 
|  | 440 |  | 
|  | 441 | smp_store_cpu_info(cpu); | 
| Paul Mackerras | 5ad5707 | 2005-11-05 10:33:55 +1100 | [diff] [blame] | 442 | set_dec(tb_ticks_per_jiffy); | 
| Andrew Morton | e4d76e1 | 2005-11-09 15:45:30 -0800 | [diff] [blame] | 443 | preempt_disable(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 444 | cpu_callin_map[cpu] = 1; | 
|  | 445 |  | 
|  | 446 | smp_ops->setup_cpu(cpu); | 
|  | 447 | if (smp_ops->take_timebase) | 
|  | 448 | smp_ops->take_timebase(); | 
|  | 449 |  | 
| Nathan Lynch | 7d4d615 | 2006-02-06 22:44:23 -0600 | [diff] [blame] | 450 | if (system_state > SYSTEM_BOOTING) | 
| Paul Mackerras | c6622f6 | 2006-02-24 10:06:59 +1100 | [diff] [blame] | 451 | snapshot_timebase(); | 
| Nathan Lynch | 7d4d615 | 2006-02-06 22:44:23 -0600 | [diff] [blame] | 452 |  | 
| Tony Breeds | d831d0b | 2007-09-21 13:26:03 +1000 | [diff] [blame] | 453 | secondary_cpu_time_init(); | 
|  | 454 |  | 
| Jens Axboe | b7d7a24 | 2008-06-26 11:22:13 +0200 | [diff] [blame] | 455 | ipi_call_lock(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 456 | cpu_set(cpu, cpu_online_map); | 
| Nathan Lynch | e2075f7 | 2008-07-27 15:24:52 +1000 | [diff] [blame] | 457 | /* Update sibling maps */ | 
|  | 458 | base = cpu_first_thread_in_core(cpu); | 
|  | 459 | for (i = 0; i < threads_per_core; i++) { | 
|  | 460 | if (cpu_is_offline(base + i)) | 
|  | 461 | continue; | 
|  | 462 | cpu_set(cpu, per_cpu(cpu_sibling_map, base + i)); | 
|  | 463 | cpu_set(base + i, per_cpu(cpu_sibling_map, cpu)); | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 464 |  | 
|  | 465 | /* cpu_core_map should be a superset of | 
|  | 466 | * cpu_sibling_map even if we don't have cache | 
|  | 467 | * information, so update the former here, too. | 
|  | 468 | */ | 
|  | 469 | cpu_set(cpu, per_cpu(cpu_core_map, base +i)); | 
|  | 470 | cpu_set(base + i, per_cpu(cpu_core_map, cpu)); | 
| Nathan Lynch | e2075f7 | 2008-07-27 15:24:52 +1000 | [diff] [blame] | 471 | } | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 472 | l2_cache = cpu_to_l2cache(cpu); | 
|  | 473 | for_each_online_cpu(i) { | 
|  | 474 | struct device_node *np = cpu_to_l2cache(i); | 
|  | 475 | if (!np) | 
|  | 476 | continue; | 
|  | 477 | if (np == l2_cache) { | 
|  | 478 | cpu_set(cpu, per_cpu(cpu_core_map, i)); | 
|  | 479 | cpu_set(i, per_cpu(cpu_core_map, cpu)); | 
|  | 480 | } | 
|  | 481 | of_node_put(np); | 
|  | 482 | } | 
|  | 483 | of_node_put(l2_cache); | 
| Jens Axboe | b7d7a24 | 2008-06-26 11:22:13 +0200 | [diff] [blame] | 484 | ipi_call_unlock(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 485 |  | 
|  | 486 | local_irq_enable(); | 
|  | 487 |  | 
|  | 488 | cpu_idle(); | 
|  | 489 | return 0; | 
|  | 490 | } | 
|  | 491 |  | 
|  | 492 | int setup_profiling_timer(unsigned int multiplier) | 
|  | 493 | { | 
|  | 494 | return 0; | 
|  | 495 | } | 
|  | 496 |  | 
|  | 497 | void __init smp_cpus_done(unsigned int max_cpus) | 
|  | 498 | { | 
|  | 499 | cpumask_t old_mask; | 
|  | 500 |  | 
|  | 501 | /* We want the setup_cpu() here to be called from CPU 0, but our | 
|  | 502 | * init thread may have been "borrowed" by another CPU in the meantime | 
|  | 503 | * se we pin us down to CPU 0 for a short while | 
|  | 504 | */ | 
|  | 505 | old_mask = current->cpus_allowed; | 
|  | 506 | set_cpus_allowed(current, cpumask_of_cpu(boot_cpuid)); | 
|  | 507 |  | 
| Benjamin Herrenschmidt | 8cffc6a | 2006-07-04 14:09:36 +1000 | [diff] [blame] | 508 | if (smp_ops) | 
|  | 509 | smp_ops->setup_cpu(boot_cpuid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 510 |  | 
|  | 511 | set_cpus_allowed(current, old_mask); | 
| Anton Blanchard | 4b703a2 | 2005-12-13 06:56:47 +1100 | [diff] [blame] | 512 |  | 
| Paul Mackerras | c6622f6 | 2006-02-24 10:06:59 +1100 | [diff] [blame] | 513 | snapshot_timebases(); | 
|  | 514 |  | 
| Anton Blanchard | 4b703a2 | 2005-12-13 06:56:47 +1100 | [diff] [blame] | 515 | dump_numa_cpu_topology(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 516 | } | 
|  | 517 |  | 
|  | 518 | #ifdef CONFIG_HOTPLUG_CPU | 
|  | 519 | int __cpu_disable(void) | 
|  | 520 | { | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 521 | struct device_node *l2_cache; | 
| Nathan Lynch | e2075f7 | 2008-07-27 15:24:52 +1000 | [diff] [blame] | 522 | int cpu = smp_processor_id(); | 
|  | 523 | int base, i; | 
|  | 524 | int err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 525 |  | 
| Nathan Lynch | e2075f7 | 2008-07-27 15:24:52 +1000 | [diff] [blame] | 526 | if (!smp_ops->cpu_disable) | 
|  | 527 | return -ENOSYS; | 
|  | 528 |  | 
|  | 529 | err = smp_ops->cpu_disable(); | 
|  | 530 | if (err) | 
|  | 531 | return err; | 
|  | 532 |  | 
|  | 533 | /* Update sibling maps */ | 
|  | 534 | base = cpu_first_thread_in_core(cpu); | 
|  | 535 | for (i = 0; i < threads_per_core; i++) { | 
|  | 536 | cpu_clear(cpu, per_cpu(cpu_sibling_map, base + i)); | 
|  | 537 | cpu_clear(base + i, per_cpu(cpu_sibling_map, cpu)); | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 538 | cpu_clear(cpu, per_cpu(cpu_core_map, base +i)); | 
|  | 539 | cpu_clear(base + i, per_cpu(cpu_core_map, cpu)); | 
| Nathan Lynch | e2075f7 | 2008-07-27 15:24:52 +1000 | [diff] [blame] | 540 | } | 
|  | 541 |  | 
| Nathan Lynch | 440a085 | 2008-07-27 15:24:53 +1000 | [diff] [blame] | 542 | l2_cache = cpu_to_l2cache(cpu); | 
|  | 543 | for_each_present_cpu(i) { | 
|  | 544 | struct device_node *np = cpu_to_l2cache(i); | 
|  | 545 | if (!np) | 
|  | 546 | continue; | 
|  | 547 | if (np == l2_cache) { | 
|  | 548 | cpu_clear(cpu, per_cpu(cpu_core_map, i)); | 
|  | 549 | cpu_clear(i, per_cpu(cpu_core_map, cpu)); | 
|  | 550 | } | 
|  | 551 | of_node_put(np); | 
|  | 552 | } | 
|  | 553 | of_node_put(l2_cache); | 
|  | 554 |  | 
|  | 555 |  | 
| Nathan Lynch | e2075f7 | 2008-07-27 15:24:52 +1000 | [diff] [blame] | 556 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 557 | } | 
|  | 558 |  | 
|  | 559 | void __cpu_die(unsigned int cpu) | 
|  | 560 | { | 
|  | 561 | if (smp_ops->cpu_die) | 
|  | 562 | smp_ops->cpu_die(cpu); | 
|  | 563 | } | 
|  | 564 | #endif |