| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  linux/arch/arm/kernel/smp.c | 
 | 3 |  * | 
 | 4 |  *  Copyright (C) 2002 ARM Limited, All Rights Reserved. | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or modify | 
 | 7 |  * it under the terms of the GNU General Public License version 2 as | 
 | 8 |  * published by the Free Software Foundation. | 
 | 9 |  */ | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 10 | #include <linux/module.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 11 | #include <linux/delay.h> | 
 | 12 | #include <linux/init.h> | 
 | 13 | #include <linux/spinlock.h> | 
 | 14 | #include <linux/sched.h> | 
 | 15 | #include <linux/interrupt.h> | 
 | 16 | #include <linux/cache.h> | 
 | 17 | #include <linux/profile.h> | 
 | 18 | #include <linux/errno.h> | 
 | 19 | #include <linux/mm.h> | 
 | 20 | #include <linux/cpu.h> | 
 | 21 | #include <linux/smp.h> | 
 | 22 | #include <linux/seq_file.h> | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 23 | #include <linux/irq.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 24 |  | 
 | 25 | #include <asm/atomic.h> | 
 | 26 | #include <asm/cacheflush.h> | 
 | 27 | #include <asm/cpu.h> | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 28 | #include <asm/mmu_context.h> | 
 | 29 | #include <asm/pgtable.h> | 
 | 30 | #include <asm/pgalloc.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | #include <asm/processor.h> | 
 | 32 | #include <asm/tlbflush.h> | 
 | 33 | #include <asm/ptrace.h> | 
 | 34 |  | 
 | 35 | /* | 
 | 36 |  * bitmask of present and online CPUs. | 
 | 37 |  * The present bitmask indicates that the CPU is physically present. | 
 | 38 |  * The online bitmask indicates that the CPU is up and running. | 
 | 39 |  */ | 
| Russell King | d12734d | 2005-07-11 17:38:36 +0100 | [diff] [blame] | 40 | cpumask_t cpu_possible_map; | 
| Greg Banks | e16b38f | 2006-10-02 02:17:40 -0700 | [diff] [blame] | 41 | EXPORT_SYMBOL(cpu_possible_map); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 42 | cpumask_t cpu_online_map; | 
| Greg Banks | e16b38f | 2006-10-02 02:17:40 -0700 | [diff] [blame] | 43 | EXPORT_SYMBOL(cpu_online_map); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 |  | 
 | 45 | /* | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 46 |  * as from 2.5, kernels no longer have an init_tasks structure | 
 | 47 |  * so we need some other way of telling a new secondary core | 
 | 48 |  * where to place its SVC stack | 
 | 49 |  */ | 
 | 50 | struct secondary_data secondary_data; | 
 | 51 |  | 
 | 52 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 |  * structures for inter-processor calls | 
 | 54 |  * - A collection of single bit ipi messages. | 
 | 55 |  */ | 
 | 56 | struct ipi_data { | 
 | 57 | 	spinlock_t lock; | 
 | 58 | 	unsigned long ipi_count; | 
 | 59 | 	unsigned long bits; | 
 | 60 | }; | 
 | 61 |  | 
 | 62 | static DEFINE_PER_CPU(struct ipi_data, ipi_data) = { | 
 | 63 | 	.lock	= SPIN_LOCK_UNLOCKED, | 
 | 64 | }; | 
 | 65 |  | 
 | 66 | enum ipi_msg_type { | 
 | 67 | 	IPI_TIMER, | 
 | 68 | 	IPI_RESCHEDULE, | 
 | 69 | 	IPI_CALL_FUNC, | 
 | 70 | 	IPI_CPU_STOP, | 
 | 71 | }; | 
 | 72 |  | 
 | 73 | struct smp_call_struct { | 
 | 74 | 	void (*func)(void *info); | 
 | 75 | 	void *info; | 
 | 76 | 	int wait; | 
 | 77 | 	cpumask_t pending; | 
 | 78 | 	cpumask_t unfinished; | 
 | 79 | }; | 
 | 80 |  | 
 | 81 | static struct smp_call_struct * volatile smp_call_function_data; | 
 | 82 | static DEFINE_SPINLOCK(smp_call_function_lock); | 
 | 83 |  | 
| Russell King | bd6f68a | 2005-07-17 21:35:41 +0100 | [diff] [blame] | 84 | int __cpuinit __cpu_up(unsigned int cpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 85 | { | 
| Russell King | 71f512e8 | 2005-11-02 21:51:40 +0000 | [diff] [blame] | 86 | 	struct cpuinfo_arm *ci = &per_cpu(cpu_data, cpu); | 
 | 87 | 	struct task_struct *idle = ci->idle; | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 88 | 	pgd_t *pgd; | 
 | 89 | 	pmd_t *pmd; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | 	int ret; | 
 | 91 |  | 
 | 92 | 	/* | 
| Russell King | 71f512e8 | 2005-11-02 21:51:40 +0000 | [diff] [blame] | 93 | 	 * Spawn a new process manually, if not already done. | 
 | 94 | 	 * Grab a pointer to its task struct so we can mess with it | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 95 | 	 */ | 
| Russell King | 71f512e8 | 2005-11-02 21:51:40 +0000 | [diff] [blame] | 96 | 	if (!idle) { | 
 | 97 | 		idle = fork_idle(cpu); | 
 | 98 | 		if (IS_ERR(idle)) { | 
 | 99 | 			printk(KERN_ERR "CPU%u: fork() failed\n", cpu); | 
 | 100 | 			return PTR_ERR(idle); | 
 | 101 | 		} | 
 | 102 | 		ci->idle = idle; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 | 	} | 
 | 104 |  | 
 | 105 | 	/* | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 106 | 	 * Allocate initial page tables to allow the new CPU to | 
 | 107 | 	 * enable the MMU safely.  This essentially means a set | 
 | 108 | 	 * of our "standard" page tables, with the addition of | 
 | 109 | 	 * a 1:1 mapping for the physical address of the kernel. | 
 | 110 | 	 */ | 
 | 111 | 	pgd = pgd_alloc(&init_mm); | 
 | 112 | 	pmd = pmd_offset(pgd, PHYS_OFFSET); | 
 | 113 | 	*pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) | | 
 | 114 | 		     PMD_TYPE_SECT | PMD_SECT_AP_WRITE); | 
 | 115 |  | 
 | 116 | 	/* | 
 | 117 | 	 * We need to tell the secondary core where to find | 
 | 118 | 	 * its stack and the page tables. | 
 | 119 | 	 */ | 
| Al Viro | 32d39a9 | 2006-01-12 01:05:58 -0800 | [diff] [blame] | 120 | 	secondary_data.stack = task_stack_page(idle) + THREAD_START_SP; | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 121 | 	secondary_data.pgdir = virt_to_phys(pgd); | 
 | 122 | 	wmb(); | 
 | 123 |  | 
 | 124 | 	/* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 125 | 	 * Now bring the CPU into our world. | 
 | 126 | 	 */ | 
 | 127 | 	ret = boot_secondary(cpu, idle); | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 128 | 	if (ret == 0) { | 
 | 129 | 		unsigned long timeout; | 
 | 130 |  | 
 | 131 | 		/* | 
 | 132 | 		 * CPU was successfully started, wait for it | 
 | 133 | 		 * to come online or time out. | 
 | 134 | 		 */ | 
 | 135 | 		timeout = jiffies + HZ; | 
 | 136 | 		while (time_before(jiffies, timeout)) { | 
 | 137 | 			if (cpu_online(cpu)) | 
 | 138 | 				break; | 
 | 139 |  | 
 | 140 | 			udelay(10); | 
 | 141 | 			barrier(); | 
 | 142 | 		} | 
 | 143 |  | 
 | 144 | 		if (!cpu_online(cpu)) | 
 | 145 | 			ret = -EIO; | 
 | 146 | 	} | 
 | 147 |  | 
| Russell King | 5d43045 | 2005-11-08 10:44:46 +0000 | [diff] [blame] | 148 | 	secondary_data.stack = NULL; | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 149 | 	secondary_data.pgdir = 0; | 
 | 150 |  | 
 | 151 | 	*pmd_offset(pgd, PHYS_OFFSET) = __pmd(0); | 
 | 152 | 	pgd_free(pgd); | 
 | 153 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 154 | 	if (ret) { | 
| Russell King | 0908db2 | 2005-06-19 19:48:16 +0100 | [diff] [blame] | 155 | 		printk(KERN_CRIT "CPU%u: processor failed to boot\n", cpu); | 
 | 156 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 157 | 		/* | 
 | 158 | 		 * FIXME: We need to clean up the new idle thread. --rmk | 
 | 159 | 		 */ | 
 | 160 | 	} | 
 | 161 |  | 
 | 162 | 	return ret; | 
 | 163 | } | 
 | 164 |  | 
| Russell King | a054a81 | 2005-11-02 22:24:33 +0000 | [diff] [blame] | 165 | #ifdef CONFIG_HOTPLUG_CPU | 
 | 166 | /* | 
 | 167 |  * __cpu_disable runs on the processor to be shutdown. | 
 | 168 |  */ | 
 | 169 | int __cpuexit __cpu_disable(void) | 
 | 170 | { | 
 | 171 | 	unsigned int cpu = smp_processor_id(); | 
 | 172 | 	struct task_struct *p; | 
 | 173 | 	int ret; | 
 | 174 |  | 
 | 175 | 	ret = mach_cpu_disable(cpu); | 
 | 176 | 	if (ret) | 
 | 177 | 		return ret; | 
 | 178 |  | 
 | 179 | 	/* | 
 | 180 | 	 * Take this CPU offline.  Once we clear this, we can't return, | 
 | 181 | 	 * and we must not schedule until we're ready to give up the cpu. | 
 | 182 | 	 */ | 
 | 183 | 	cpu_clear(cpu, cpu_online_map); | 
 | 184 |  | 
 | 185 | 	/* | 
 | 186 | 	 * OK - migrate IRQs away from this CPU | 
 | 187 | 	 */ | 
 | 188 | 	migrate_irqs(); | 
 | 189 |  | 
 | 190 | 	/* | 
| Russell King | 37ee16a | 2005-11-08 19:08:05 +0000 | [diff] [blame] | 191 | 	 * Stop the local timer for this CPU. | 
 | 192 | 	 */ | 
 | 193 | 	local_timer_stop(cpu); | 
 | 194 |  | 
 | 195 | 	/* | 
| Russell King | a054a81 | 2005-11-02 22:24:33 +0000 | [diff] [blame] | 196 | 	 * Flush user cache and TLB mappings, and then remove this CPU | 
 | 197 | 	 * from the vm mask set of all processes. | 
 | 198 | 	 */ | 
 | 199 | 	flush_cache_all(); | 
 | 200 | 	local_flush_tlb_all(); | 
 | 201 |  | 
 | 202 | 	read_lock(&tasklist_lock); | 
 | 203 | 	for_each_process(p) { | 
 | 204 | 		if (p->mm) | 
 | 205 | 			cpu_clear(cpu, p->mm->cpu_vm_mask); | 
 | 206 | 	} | 
 | 207 | 	read_unlock(&tasklist_lock); | 
 | 208 |  | 
 | 209 | 	return 0; | 
 | 210 | } | 
 | 211 |  | 
 | 212 | /* | 
 | 213 |  * called on the thread which is asking for a CPU to be shutdown - | 
 | 214 |  * waits until shutdown has completed, or it is timed out. | 
 | 215 |  */ | 
 | 216 | void __cpuexit __cpu_die(unsigned int cpu) | 
 | 217 | { | 
 | 218 | 	if (!platform_cpu_kill(cpu)) | 
 | 219 | 		printk("CPU%u: unable to kill\n", cpu); | 
 | 220 | } | 
 | 221 |  | 
 | 222 | /* | 
 | 223 |  * Called from the idle thread for the CPU which has been shutdown. | 
 | 224 |  * | 
 | 225 |  * Note that we disable IRQs here, but do not re-enable them | 
 | 226 |  * before returning to the caller. This is also the behaviour | 
 | 227 |  * of the other hotplug-cpu capable cores, so presumably coming | 
 | 228 |  * out of idle fixes this. | 
 | 229 |  */ | 
 | 230 | void __cpuexit cpu_die(void) | 
 | 231 | { | 
 | 232 | 	unsigned int cpu = smp_processor_id(); | 
 | 233 |  | 
 | 234 | 	local_irq_disable(); | 
 | 235 | 	idle_task_exit(); | 
 | 236 |  | 
 | 237 | 	/* | 
 | 238 | 	 * actual CPU shutdown procedure is at least platform (if not | 
 | 239 | 	 * CPU) specific | 
 | 240 | 	 */ | 
 | 241 | 	platform_cpu_die(cpu); | 
 | 242 |  | 
 | 243 | 	/* | 
 | 244 | 	 * Do not return to the idle loop - jump back to the secondary | 
 | 245 | 	 * cpu initialisation.  There's some initialisation which needs | 
 | 246 | 	 * to be repeated to undo the effects of taking the CPU offline. | 
 | 247 | 	 */ | 
 | 248 | 	__asm__("mov	sp, %0\n" | 
 | 249 | 	"	b	secondary_start_kernel" | 
 | 250 | 		: | 
| Al Viro | 32d39a9 | 2006-01-12 01:05:58 -0800 | [diff] [blame] | 251 | 		: "r" (task_stack_page(current) + THREAD_SIZE - 8)); | 
| Russell King | a054a81 | 2005-11-02 22:24:33 +0000 | [diff] [blame] | 252 | } | 
 | 253 | #endif /* CONFIG_HOTPLUG_CPU */ | 
 | 254 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 255 | /* | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 256 |  * This is the secondary CPU boot entry.  We're using this CPUs | 
 | 257 |  * idle thread stack, but a set of temporary page tables. | 
 | 258 |  */ | 
| Russell King | bd6f68a | 2005-07-17 21:35:41 +0100 | [diff] [blame] | 259 | asmlinkage void __cpuinit secondary_start_kernel(void) | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 260 | { | 
 | 261 | 	struct mm_struct *mm = &init_mm; | 
| Russell King | da2660d | 2005-11-12 17:21:47 +0000 | [diff] [blame] | 262 | 	unsigned int cpu = smp_processor_id(); | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 263 |  | 
 | 264 | 	printk("CPU%u: Booted secondary processor\n", cpu); | 
 | 265 |  | 
 | 266 | 	/* | 
 | 267 | 	 * All kernel threads share the same mm context; grab a | 
 | 268 | 	 * reference and switch to it. | 
 | 269 | 	 */ | 
 | 270 | 	atomic_inc(&mm->mm_users); | 
 | 271 | 	atomic_inc(&mm->mm_count); | 
 | 272 | 	current->active_mm = mm; | 
 | 273 | 	cpu_set(cpu, mm->cpu_vm_mask); | 
 | 274 | 	cpu_switch_mm(mm->pgd, mm); | 
 | 275 | 	enter_lazy_tlb(mm, current); | 
| Russell King | 505d7b1 | 2005-07-28 20:32:47 +0100 | [diff] [blame] | 276 | 	local_flush_tlb_all(); | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 277 |  | 
 | 278 | 	cpu_init(); | 
| Nick Piggin | 5bfb5d6 | 2005-11-08 21:39:01 -0800 | [diff] [blame] | 279 | 	preempt_disable(); | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 280 |  | 
 | 281 | 	/* | 
 | 282 | 	 * Give the platform a chance to do its own initialisation. | 
 | 283 | 	 */ | 
 | 284 | 	platform_secondary_init(cpu); | 
 | 285 |  | 
 | 286 | 	/* | 
 | 287 | 	 * Enable local interrupts. | 
 | 288 | 	 */ | 
 | 289 | 	local_irq_enable(); | 
 | 290 | 	local_fiq_enable(); | 
 | 291 |  | 
 | 292 | 	calibrate_delay(); | 
 | 293 |  | 
 | 294 | 	smp_store_cpu_info(cpu); | 
 | 295 |  | 
 | 296 | 	/* | 
 | 297 | 	 * OK, now it's safe to let the boot CPU continue | 
 | 298 | 	 */ | 
 | 299 | 	cpu_set(cpu, cpu_online_map); | 
 | 300 |  | 
 | 301 | 	/* | 
| Russell King | 37ee16a | 2005-11-08 19:08:05 +0000 | [diff] [blame] | 302 | 	 * Setup local timer for this CPU. | 
 | 303 | 	 */ | 
 | 304 | 	local_timer_setup(cpu); | 
 | 305 |  | 
 | 306 | 	/* | 
| Russell King | e65f38e | 2005-06-18 09:33:31 +0100 | [diff] [blame] | 307 | 	 * OK, it's off to the idle thread for us | 
 | 308 | 	 */ | 
 | 309 | 	cpu_idle(); | 
 | 310 | } | 
 | 311 |  | 
 | 312 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 313 |  * Called by both boot and secondaries to move global data into | 
 | 314 |  * per-processor storage. | 
 | 315 |  */ | 
| Russell King | bd6f68a | 2005-07-17 21:35:41 +0100 | [diff] [blame] | 316 | void __cpuinit smp_store_cpu_info(unsigned int cpuid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 317 | { | 
 | 318 | 	struct cpuinfo_arm *cpu_info = &per_cpu(cpu_data, cpuid); | 
 | 319 |  | 
 | 320 | 	cpu_info->loops_per_jiffy = loops_per_jiffy; | 
 | 321 | } | 
 | 322 |  | 
 | 323 | void __init smp_cpus_done(unsigned int max_cpus) | 
 | 324 | { | 
 | 325 | 	int cpu; | 
 | 326 | 	unsigned long bogosum = 0; | 
 | 327 |  | 
 | 328 | 	for_each_online_cpu(cpu) | 
 | 329 | 		bogosum += per_cpu(cpu_data, cpu).loops_per_jiffy; | 
 | 330 |  | 
 | 331 | 	printk(KERN_INFO "SMP: Total of %d processors activated " | 
 | 332 | 	       "(%lu.%02lu BogoMIPS).\n", | 
 | 333 | 	       num_online_cpus(), | 
 | 334 | 	       bogosum / (500000/HZ), | 
 | 335 | 	       (bogosum / (5000/HZ)) % 100); | 
 | 336 | } | 
 | 337 |  | 
 | 338 | void __init smp_prepare_boot_cpu(void) | 
 | 339 | { | 
 | 340 | 	unsigned int cpu = smp_processor_id(); | 
 | 341 |  | 
| Russell King | 71f512e8 | 2005-11-02 21:51:40 +0000 | [diff] [blame] | 342 | 	per_cpu(cpu_data, cpu).idle = current; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 343 | } | 
 | 344 |  | 
 | 345 | static void send_ipi_message(cpumask_t callmap, enum ipi_msg_type msg) | 
 | 346 | { | 
 | 347 | 	unsigned long flags; | 
 | 348 | 	unsigned int cpu; | 
 | 349 |  | 
 | 350 | 	local_irq_save(flags); | 
 | 351 |  | 
 | 352 | 	for_each_cpu_mask(cpu, callmap) { | 
 | 353 | 		struct ipi_data *ipi = &per_cpu(ipi_data, cpu); | 
 | 354 |  | 
 | 355 | 		spin_lock(&ipi->lock); | 
 | 356 | 		ipi->bits |= 1 << msg; | 
 | 357 | 		spin_unlock(&ipi->lock); | 
 | 358 | 	} | 
 | 359 |  | 
 | 360 | 	/* | 
 | 361 | 	 * Call the platform specific cross-CPU call function. | 
 | 362 | 	 */ | 
 | 363 | 	smp_cross_call(callmap); | 
 | 364 |  | 
 | 365 | 	local_irq_restore(flags); | 
 | 366 | } | 
 | 367 |  | 
 | 368 | /* | 
 | 369 |  * You must not call this function with disabled interrupts, from a | 
 | 370 |  * hardware interrupt handler, nor from a bottom half handler. | 
 | 371 |  */ | 
| Russell King | 5d43045 | 2005-11-08 10:44:46 +0000 | [diff] [blame] | 372 | static int smp_call_function_on_cpu(void (*func)(void *info), void *info, | 
 | 373 | 				    int retry, int wait, cpumask_t callmap) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 374 | { | 
 | 375 | 	struct smp_call_struct data; | 
 | 376 | 	unsigned long timeout; | 
 | 377 | 	int ret = 0; | 
 | 378 |  | 
 | 379 | 	data.func = func; | 
 | 380 | 	data.info = info; | 
 | 381 | 	data.wait = wait; | 
 | 382 |  | 
 | 383 | 	cpu_clear(smp_processor_id(), callmap); | 
 | 384 | 	if (cpus_empty(callmap)) | 
 | 385 | 		goto out; | 
 | 386 |  | 
 | 387 | 	data.pending = callmap; | 
 | 388 | 	if (wait) | 
 | 389 | 		data.unfinished = callmap; | 
 | 390 |  | 
 | 391 | 	/* | 
 | 392 | 	 * try to get the mutex on smp_call_function_data | 
 | 393 | 	 */ | 
 | 394 | 	spin_lock(&smp_call_function_lock); | 
 | 395 | 	smp_call_function_data = &data; | 
 | 396 |  | 
 | 397 | 	send_ipi_message(callmap, IPI_CALL_FUNC); | 
 | 398 |  | 
 | 399 | 	timeout = jiffies + HZ; | 
 | 400 | 	while (!cpus_empty(data.pending) && time_before(jiffies, timeout)) | 
 | 401 | 		barrier(); | 
 | 402 |  | 
 | 403 | 	/* | 
 | 404 | 	 * did we time out? | 
 | 405 | 	 */ | 
 | 406 | 	if (!cpus_empty(data.pending)) { | 
 | 407 | 		/* | 
 | 408 | 		 * this may be causing our panic - report it | 
 | 409 | 		 */ | 
 | 410 | 		printk(KERN_CRIT | 
 | 411 | 		       "CPU%u: smp_call_function timeout for %p(%p)\n" | 
 | 412 | 		       "      callmap %lx pending %lx, %swait\n", | 
| Russell King | 273c2cd | 2005-11-02 21:54:14 +0000 | [diff] [blame] | 413 | 		       smp_processor_id(), func, info, *cpus_addr(callmap), | 
 | 414 | 		       *cpus_addr(data.pending), wait ? "" : "no "); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 415 |  | 
 | 416 | 		/* | 
 | 417 | 		 * TRACE | 
 | 418 | 		 */ | 
 | 419 | 		timeout = jiffies + (5 * HZ); | 
 | 420 | 		while (!cpus_empty(data.pending) && time_before(jiffies, timeout)) | 
 | 421 | 			barrier(); | 
 | 422 |  | 
 | 423 | 		if (cpus_empty(data.pending)) | 
 | 424 | 			printk(KERN_CRIT "     RESOLVED\n"); | 
 | 425 | 		else | 
 | 426 | 			printk(KERN_CRIT "     STILL STUCK\n"); | 
 | 427 | 	} | 
 | 428 |  | 
 | 429 | 	/* | 
 | 430 | 	 * whatever happened, we're done with the data, so release it | 
 | 431 | 	 */ | 
 | 432 | 	smp_call_function_data = NULL; | 
 | 433 | 	spin_unlock(&smp_call_function_lock); | 
 | 434 |  | 
 | 435 | 	if (!cpus_empty(data.pending)) { | 
 | 436 | 		ret = -ETIMEDOUT; | 
 | 437 | 		goto out; | 
 | 438 | 	} | 
 | 439 |  | 
 | 440 | 	if (wait) | 
 | 441 | 		while (!cpus_empty(data.unfinished)) | 
 | 442 | 			barrier(); | 
 | 443 |  out: | 
 | 444 |  | 
 | 445 | 	return 0; | 
 | 446 | } | 
 | 447 |  | 
 | 448 | int smp_call_function(void (*func)(void *info), void *info, int retry, | 
 | 449 |                       int wait) | 
 | 450 | { | 
 | 451 | 	return smp_call_function_on_cpu(func, info, retry, wait, | 
 | 452 | 					cpu_online_map); | 
 | 453 | } | 
 | 454 |  | 
 | 455 | void show_ipi_list(struct seq_file *p) | 
 | 456 | { | 
 | 457 | 	unsigned int cpu; | 
 | 458 |  | 
 | 459 | 	seq_puts(p, "IPI:"); | 
 | 460 |  | 
| Russell King | e11b223 | 2005-07-11 19:26:31 +0100 | [diff] [blame] | 461 | 	for_each_present_cpu(cpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 462 | 		seq_printf(p, " %10lu", per_cpu(ipi_data, cpu).ipi_count); | 
 | 463 |  | 
 | 464 | 	seq_putc(p, '\n'); | 
 | 465 | } | 
 | 466 |  | 
| Russell King | 37ee16a | 2005-11-08 19:08:05 +0000 | [diff] [blame] | 467 | void show_local_irqs(struct seq_file *p) | 
 | 468 | { | 
 | 469 | 	unsigned int cpu; | 
 | 470 |  | 
 | 471 | 	seq_printf(p, "LOC: "); | 
 | 472 |  | 
 | 473 | 	for_each_present_cpu(cpu) | 
 | 474 | 		seq_printf(p, "%10u ", irq_stat[cpu].local_timer_irqs); | 
 | 475 |  | 
 | 476 | 	seq_putc(p, '\n'); | 
 | 477 | } | 
 | 478 |  | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 479 | static void ipi_timer(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 480 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 481 | 	irq_enter(); | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 482 | 	profile_tick(CPU_PROFILING); | 
 | 483 | 	update_process_times(user_mode(get_irq_regs())); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 484 | 	irq_exit(); | 
 | 485 | } | 
 | 486 |  | 
| Russell King | 37ee16a | 2005-11-08 19:08:05 +0000 | [diff] [blame] | 487 | #ifdef CONFIG_LOCAL_TIMERS | 
 | 488 | asmlinkage void do_local_timer(struct pt_regs *regs) | 
 | 489 | { | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 490 | 	struct pt_regs *old_regs = set_irq_regs(regs); | 
| Russell King | 37ee16a | 2005-11-08 19:08:05 +0000 | [diff] [blame] | 491 | 	int cpu = smp_processor_id(); | 
 | 492 |  | 
 | 493 | 	if (local_timer_ack()) { | 
 | 494 | 		irq_stat[cpu].local_timer_irqs++; | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 495 | 		ipi_timer(); | 
| Russell King | 37ee16a | 2005-11-08 19:08:05 +0000 | [diff] [blame] | 496 | 	} | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 497 |  | 
 | 498 | 	set_irq_regs(old_regs); | 
| Russell King | 37ee16a | 2005-11-08 19:08:05 +0000 | [diff] [blame] | 499 | } | 
 | 500 | #endif | 
 | 501 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 502 | /* | 
 | 503 |  * ipi_call_function - handle IPI from smp_call_function() | 
 | 504 |  * | 
 | 505 |  * Note that we copy data out of the cross-call structure and then | 
 | 506 |  * let the caller know that we're here and have done with their data | 
 | 507 |  */ | 
 | 508 | static void ipi_call_function(unsigned int cpu) | 
 | 509 | { | 
 | 510 | 	struct smp_call_struct *data = smp_call_function_data; | 
 | 511 | 	void (*func)(void *info) = data->func; | 
 | 512 | 	void *info = data->info; | 
 | 513 | 	int wait = data->wait; | 
 | 514 |  | 
 | 515 | 	cpu_clear(cpu, data->pending); | 
 | 516 |  | 
 | 517 | 	func(info); | 
 | 518 |  | 
 | 519 | 	if (wait) | 
 | 520 | 		cpu_clear(cpu, data->unfinished); | 
 | 521 | } | 
 | 522 |  | 
 | 523 | static DEFINE_SPINLOCK(stop_lock); | 
 | 524 |  | 
 | 525 | /* | 
 | 526 |  * ipi_cpu_stop - handle IPI from smp_send_stop() | 
 | 527 |  */ | 
 | 528 | static void ipi_cpu_stop(unsigned int cpu) | 
 | 529 | { | 
 | 530 | 	spin_lock(&stop_lock); | 
 | 531 | 	printk(KERN_CRIT "CPU%u: stopping\n", cpu); | 
 | 532 | 	dump_stack(); | 
 | 533 | 	spin_unlock(&stop_lock); | 
 | 534 |  | 
 | 535 | 	cpu_clear(cpu, cpu_online_map); | 
 | 536 |  | 
 | 537 | 	local_fiq_disable(); | 
 | 538 | 	local_irq_disable(); | 
 | 539 |  | 
 | 540 | 	while (1) | 
 | 541 | 		cpu_relax(); | 
 | 542 | } | 
 | 543 |  | 
 | 544 | /* | 
 | 545 |  * Main handler for inter-processor interrupts | 
 | 546 |  * | 
 | 547 |  * For ARM, the ipimask now only identifies a single | 
 | 548 |  * category of IPI (Bit 1 IPIs have been replaced by a | 
 | 549 |  * different mechanism): | 
 | 550 |  * | 
 | 551 |  *  Bit 0 - Inter-processor function call | 
 | 552 |  */ | 
| Russell King | 2c25013 | 2005-11-08 14:44:15 +0000 | [diff] [blame] | 553 | asmlinkage void do_IPI(struct pt_regs *regs) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 554 | { | 
 | 555 | 	unsigned int cpu = smp_processor_id(); | 
 | 556 | 	struct ipi_data *ipi = &per_cpu(ipi_data, cpu); | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 557 | 	struct pt_regs *old_regs = set_irq_regs(regs); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 558 |  | 
 | 559 | 	ipi->ipi_count++; | 
 | 560 |  | 
 | 561 | 	for (;;) { | 
 | 562 | 		unsigned long msgs; | 
 | 563 |  | 
 | 564 | 		spin_lock(&ipi->lock); | 
 | 565 | 		msgs = ipi->bits; | 
 | 566 | 		ipi->bits = 0; | 
 | 567 | 		spin_unlock(&ipi->lock); | 
 | 568 |  | 
 | 569 | 		if (!msgs) | 
 | 570 | 			break; | 
 | 571 |  | 
 | 572 | 		do { | 
 | 573 | 			unsigned nextmsg; | 
 | 574 |  | 
 | 575 | 			nextmsg = msgs & -msgs; | 
 | 576 | 			msgs &= ~nextmsg; | 
 | 577 | 			nextmsg = ffz(~nextmsg); | 
 | 578 |  | 
 | 579 | 			switch (nextmsg) { | 
 | 580 | 			case IPI_TIMER: | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 581 | 				ipi_timer(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 582 | 				break; | 
 | 583 |  | 
 | 584 | 			case IPI_RESCHEDULE: | 
 | 585 | 				/* | 
 | 586 | 				 * nothing more to do - eveything is | 
 | 587 | 				 * done on the interrupt return path | 
 | 588 | 				 */ | 
 | 589 | 				break; | 
 | 590 |  | 
 | 591 | 			case IPI_CALL_FUNC: | 
 | 592 | 				ipi_call_function(cpu); | 
 | 593 | 				break; | 
 | 594 |  | 
 | 595 | 			case IPI_CPU_STOP: | 
 | 596 | 				ipi_cpu_stop(cpu); | 
 | 597 | 				break; | 
 | 598 |  | 
 | 599 | 			default: | 
 | 600 | 				printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n", | 
 | 601 | 				       cpu, nextmsg); | 
 | 602 | 				break; | 
 | 603 | 			} | 
 | 604 | 		} while (msgs); | 
 | 605 | 	} | 
| Russell King | c97d486 | 2006-10-25 13:59:16 +0100 | [diff] [blame] | 606 |  | 
 | 607 | 	set_irq_regs(old_regs); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 608 | } | 
 | 609 |  | 
 | 610 | void smp_send_reschedule(int cpu) | 
 | 611 | { | 
 | 612 | 	send_ipi_message(cpumask_of_cpu(cpu), IPI_RESCHEDULE); | 
 | 613 | } | 
 | 614 |  | 
 | 615 | void smp_send_timer(void) | 
 | 616 | { | 
 | 617 | 	cpumask_t mask = cpu_online_map; | 
 | 618 | 	cpu_clear(smp_processor_id(), mask); | 
 | 619 | 	send_ipi_message(mask, IPI_TIMER); | 
 | 620 | } | 
 | 621 |  | 
 | 622 | void smp_send_stop(void) | 
 | 623 | { | 
 | 624 | 	cpumask_t mask = cpu_online_map; | 
 | 625 | 	cpu_clear(smp_processor_id(), mask); | 
 | 626 | 	send_ipi_message(mask, IPI_CPU_STOP); | 
 | 627 | } | 
 | 628 |  | 
 | 629 | /* | 
 | 630 |  * not supported here | 
 | 631 |  */ | 
 | 632 | int __init setup_profiling_timer(unsigned int multiplier) | 
 | 633 | { | 
 | 634 | 	return -EINVAL; | 
 | 635 | } | 
| Russell King | 4b0ef3b | 2005-06-28 13:49:16 +0100 | [diff] [blame] | 636 |  | 
 | 637 | static int | 
 | 638 | on_each_cpu_mask(void (*func)(void *), void *info, int retry, int wait, | 
 | 639 | 		 cpumask_t mask) | 
 | 640 | { | 
 | 641 | 	int ret = 0; | 
 | 642 |  | 
 | 643 | 	preempt_disable(); | 
 | 644 |  | 
 | 645 | 	ret = smp_call_function_on_cpu(func, info, retry, wait, mask); | 
 | 646 | 	if (cpu_isset(smp_processor_id(), mask)) | 
 | 647 | 		func(info); | 
 | 648 |  | 
 | 649 | 	preempt_enable(); | 
 | 650 |  | 
 | 651 | 	return ret; | 
 | 652 | } | 
 | 653 |  | 
 | 654 | /**********************************************************************/ | 
 | 655 |  | 
 | 656 | /* | 
 | 657 |  * TLB operations | 
 | 658 |  */ | 
 | 659 | struct tlb_args { | 
 | 660 | 	struct vm_area_struct *ta_vma; | 
 | 661 | 	unsigned long ta_start; | 
 | 662 | 	unsigned long ta_end; | 
 | 663 | }; | 
 | 664 |  | 
 | 665 | static inline void ipi_flush_tlb_all(void *ignored) | 
 | 666 | { | 
 | 667 | 	local_flush_tlb_all(); | 
 | 668 | } | 
 | 669 |  | 
 | 670 | static inline void ipi_flush_tlb_mm(void *arg) | 
 | 671 | { | 
 | 672 | 	struct mm_struct *mm = (struct mm_struct *)arg; | 
 | 673 |  | 
 | 674 | 	local_flush_tlb_mm(mm); | 
 | 675 | } | 
 | 676 |  | 
 | 677 | static inline void ipi_flush_tlb_page(void *arg) | 
 | 678 | { | 
 | 679 | 	struct tlb_args *ta = (struct tlb_args *)arg; | 
 | 680 |  | 
 | 681 | 	local_flush_tlb_page(ta->ta_vma, ta->ta_start); | 
 | 682 | } | 
 | 683 |  | 
 | 684 | static inline void ipi_flush_tlb_kernel_page(void *arg) | 
 | 685 | { | 
 | 686 | 	struct tlb_args *ta = (struct tlb_args *)arg; | 
 | 687 |  | 
 | 688 | 	local_flush_tlb_kernel_page(ta->ta_start); | 
 | 689 | } | 
 | 690 |  | 
 | 691 | static inline void ipi_flush_tlb_range(void *arg) | 
 | 692 | { | 
 | 693 | 	struct tlb_args *ta = (struct tlb_args *)arg; | 
 | 694 |  | 
 | 695 | 	local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end); | 
 | 696 | } | 
 | 697 |  | 
 | 698 | static inline void ipi_flush_tlb_kernel_range(void *arg) | 
 | 699 | { | 
 | 700 | 	struct tlb_args *ta = (struct tlb_args *)arg; | 
 | 701 |  | 
 | 702 | 	local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end); | 
 | 703 | } | 
 | 704 |  | 
 | 705 | void flush_tlb_all(void) | 
 | 706 | { | 
 | 707 | 	on_each_cpu(ipi_flush_tlb_all, NULL, 1, 1); | 
 | 708 | } | 
 | 709 |  | 
 | 710 | void flush_tlb_mm(struct mm_struct *mm) | 
 | 711 | { | 
 | 712 | 	cpumask_t mask = mm->cpu_vm_mask; | 
 | 713 |  | 
 | 714 | 	on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, 1, mask); | 
 | 715 | } | 
 | 716 |  | 
 | 717 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) | 
 | 718 | { | 
 | 719 | 	cpumask_t mask = vma->vm_mm->cpu_vm_mask; | 
 | 720 | 	struct tlb_args ta; | 
 | 721 |  | 
 | 722 | 	ta.ta_vma = vma; | 
 | 723 | 	ta.ta_start = uaddr; | 
 | 724 |  | 
 | 725 | 	on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, 1, mask); | 
 | 726 | } | 
 | 727 |  | 
 | 728 | void flush_tlb_kernel_page(unsigned long kaddr) | 
 | 729 | { | 
 | 730 | 	struct tlb_args ta; | 
 | 731 |  | 
 | 732 | 	ta.ta_start = kaddr; | 
 | 733 |  | 
 | 734 | 	on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1, 1); | 
 | 735 | } | 
 | 736 |  | 
 | 737 | void flush_tlb_range(struct vm_area_struct *vma, | 
 | 738 |                      unsigned long start, unsigned long end) | 
 | 739 | { | 
 | 740 | 	cpumask_t mask = vma->vm_mm->cpu_vm_mask; | 
 | 741 | 	struct tlb_args ta; | 
 | 742 |  | 
 | 743 | 	ta.ta_vma = vma; | 
 | 744 | 	ta.ta_start = start; | 
 | 745 | 	ta.ta_end = end; | 
 | 746 |  | 
 | 747 | 	on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, 1, mask); | 
 | 748 | } | 
 | 749 |  | 
 | 750 | void flush_tlb_kernel_range(unsigned long start, unsigned long end) | 
 | 751 | { | 
 | 752 | 	struct tlb_args ta; | 
 | 753 |  | 
 | 754 | 	ta.ta_start = start; | 
 | 755 | 	ta.ta_end = end; | 
 | 756 |  | 
 | 757 | 	on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1, 1); | 
 | 758 | } |