From 6bf7bd6967b1cdde1fe953b0edb951966799fb44 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 2 Nov 2005 14:11:35 +0000 Subject: [ARM] Fix mm initialisation with write buffered write allocate caches It seems that without the extra tlb flush, we may end up faulting during the early kernel initialisation because the TLB can't see the updated page tables. Signed-off-by: Russell King --- arch/arm/mm/init.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index fd079ff1fc53..c168f322ef8c 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -486,10 +486,17 @@ static void __init devicemaps_init(struct machine_desc *mdesc) /* * Ask the machine support to map in the statically mapped devices. - * After this point, we can start to touch devices again. */ if (mdesc->map_io) mdesc->map_io(); + + /* + * Finally flush the tlb again - this ensures that we're in a + * consistent state wrt the writebuffer if the writebuffer needs + * draining. After this point, we can start to touch devices + * again. + */ + local_flush_tlb_all(); } /* -- cgit v1.2.3-58-ga151 From 1ffedce7e83540e2d2fe1cacd1922ee337073d28 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 2 Nov 2005 14:14:37 +0000 Subject: [ARM] Fix Realview machine for patch 3016/1 3016/1 changed the map_desc structure to take a PFN instead of a physical address. Fixup Realview machine support for this change. Signed-off-by: Russell King --- arch/arm/mach-realview/realview_eb.c | 44 ++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c index 01b264be5029..267bb07e39b7 100644 --- a/arch/arm/mach-realview/realview_eb.c +++ b/arch/arm/mach-realview/realview_eb.c @@ -43,14 +43,44 @@ #include "clock.h" static struct map_desc realview_eb_io_desc[] __initdata = { - { IO_ADDRESS(REALVIEW_SYS_BASE), REALVIEW_SYS_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(REALVIEW_GIC_CPU_BASE), REALVIEW_GIC_CPU_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(REALVIEW_GIC_DIST_BASE), REALVIEW_GIC_DIST_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(REALVIEW_SCTL_BASE), REALVIEW_SCTL_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(REALVIEW_TIMER0_1_BASE), REALVIEW_TIMER0_1_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(REALVIEW_TIMER2_3_BASE), REALVIEW_TIMER2_3_BASE, SZ_4K, MT_DEVICE }, + { + .virtual = IO_ADDRESS(REALVIEW_SYS_BASE), + .pfn = __phys_to_pfn(REALVIEW_SYS_BASE), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = IO_ADDRESS(REALVIEW_GIC_CPU_BASE), + .pfn = __phys_to_pfn(REALVIEW_GIC_CPU_BASE), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = IO_ADDRESS(REALVIEW_GIC_DIST_BASE), + .pfn = __phys_to_pfn(REALVIEW_GIC_DIST_BASE), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = IO_ADDRESS(REALVIEW_SCTL_BASE), + .pfn = __phys_to_pfn(REALVIEW_SCTL_BASE), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = IO_ADDRESS(REALVIEW_TIMER0_1_BASE), + .pfn = __phys_to_pfn(REALVIEW_TIMER0_1_BASE), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = IO_ADDRESS(REALVIEW_TIMER2_3_BASE), + .pfn = __phys_to_pfn(REALVIEW_TIMER2_3_BASE), + .length = SZ_4K, + .type = MT_DEVICE, + }, #ifdef CONFIG_DEBUG_LL - { IO_ADDRESS(REALVIEW_UART0_BASE), REALVIEW_UART0_BASE, SZ_4K, MT_DEVICE }, + { + .virtual = IO_ADDRESS(REALVIEW_UART0_BASE), + .pfn = __phys_to_pfn(REALVIEW_UART0_BASE), + .length = SZ_4K, + .type = MT_DEVICE, + } #endif }; -- cgit v1.2.3-58-ga151 From 71f512e89704f5aa6fc0b97e4a719184080b8938 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 2 Nov 2005 21:51:40 +0000 Subject: [ARM SMP] Track CPU idle threads Track the idle thread task_struct for each CPU. Signed-off-by: Russell King --- arch/arm/kernel/smp.c | 20 +++++++++++++------- include/asm-arm/cpu.h | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 826164945747..45877f5d5717 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -80,19 +80,23 @@ static DEFINE_SPINLOCK(smp_call_function_lock); int __cpuinit __cpu_up(unsigned int cpu) { - struct task_struct *idle; + struct cpuinfo_arm *ci = &per_cpu(cpu_data, cpu); + struct task_struct *idle = ci->idle; pgd_t *pgd; pmd_t *pmd; int ret; /* - * Spawn a new process manually. Grab a pointer to - * its task struct so we can mess with it + * Spawn a new process manually, if not already done. + * Grab a pointer to its task struct so we can mess with it */ - idle = fork_idle(cpu); - if (IS_ERR(idle)) { - printk(KERN_ERR "CPU%u: fork() failed\n", cpu); - return PTR_ERR(idle); + if (!idle) { + idle = fork_idle(cpu); + if (IS_ERR(idle)) { + printk(KERN_ERR "CPU%u: fork() failed\n", cpu); + return PTR_ERR(idle); + } + ci->idle = idle; } /* @@ -236,6 +240,8 @@ void __init smp_prepare_boot_cpu(void) { unsigned int cpu = smp_processor_id(); + per_cpu(cpu_data, cpu).idle = current; + cpu_set(cpu, cpu_possible_map); cpu_set(cpu, cpu_present_map); cpu_set(cpu, cpu_online_map); diff --git a/include/asm-arm/cpu.h b/include/asm-arm/cpu.h index fcbdd40cb667..751bc7462074 100644 --- a/include/asm-arm/cpu.h +++ b/include/asm-arm/cpu.h @@ -16,6 +16,7 @@ struct cpuinfo_arm { struct cpu cpu; #ifdef CONFIG_SMP + struct task_struct *idle; unsigned int loops_per_jiffy; #endif }; -- cgit v1.2.3-58-ga151 From 273c2cdb2b6d6743d85ddbde82e71f8adbf5bf10 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 2 Nov 2005 21:54:14 +0000 Subject: [ARM SMP] Fix a couple of warnings Use *cpus_addr() to display the mask of pending/to be called CPUs. Signed-off-by: Russell King --- arch/arm/kernel/smp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 45877f5d5717..b40915dcd533 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -315,8 +315,8 @@ int smp_call_function_on_cpu(void (*func)(void *info), void *info, int retry, printk(KERN_CRIT "CPU%u: smp_call_function timeout for %p(%p)\n" " callmap %lx pending %lx, %swait\n", - smp_processor_id(), func, info, callmap, data.pending, - wait ? "" : "no "); + smp_processor_id(), func, info, *cpus_addr(callmap), + *cpus_addr(data.pending), wait ? "" : "no "); /* * TRACE -- cgit v1.2.3-58-ga151 From a054a811597a17ffbe92bc4db04a4dc2f1b1ea55 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 2 Nov 2005 22:24:33 +0000 Subject: [ARM SMP] Add hotplug CPU infrastructure This patch adds the infrastructure to support hotplug CPU on ARM platforms. Signed-off-by: Russell King --- arch/arm/Kconfig | 7 ++++ arch/arm/kernel/irq.c | 31 +++++++++++++++++ arch/arm/kernel/process.c | 9 +++++ arch/arm/kernel/smp.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++ include/asm-arm/irq.h | 1 + include/asm-arm/smp.h | 10 ++++++ 6 files changed, 143 insertions(+) (limited to 'arch') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index dc6d8342e5e6..6b12d71978de 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -349,6 +349,13 @@ config NR_CPUS depends on SMP default "4" +config HOTPLUG_CPU + bool "Support for hot-pluggable CPUs (EXPERIMENTAL)" + depends on SMP && HOTPLUG && EXPERIMENTAL + help + Say Y here to experiment with turning CPUs off and on. CPUs + can be controlled through /sys/devices/system/cpu. + config PREEMPT bool "Preemptible Kernel (EXPERIMENTAL)" depends on EXPERIMENTAL diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 3284118f356b..9def4404e1f2 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -1050,3 +1050,34 @@ static int __init noirqdebug_setup(char *str) } __setup("noirqdebug", noirqdebug_setup); + +#ifdef CONFIG_HOTPLUG_CPU +/* + * The CPU has been marked offline. Migrate IRQs off this CPU. If + * the affinity settings do not allow other CPUs, force them onto any + * available CPU. + */ +void migrate_irqs(void) +{ + unsigned int i, cpu = smp_processor_id(); + + for (i = 0; i < NR_IRQS; i++) { + struct irqdesc *desc = irq_desc + i; + + if (desc->cpu == cpu) { + unsigned int newcpu = any_online_cpu(desc->affinity); + + if (newcpu == NR_CPUS) { + if (printk_ratelimit()) + printk(KERN_INFO "IRQ%u no longer affine to CPU%u\n", + i, cpu); + + cpus_setall(desc->affinity); + newcpu = any_online_cpu(desc->affinity); + } + + route_irq(desc, i, newcpu); + } + } +} +#endif /* CONFIG_HOTPLUG_CPU */ diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 409db6d5ec99..ba298277becd 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,14 @@ void cpu_idle(void) /* endless idle loop with no priority at all */ while (1) { void (*idle)(void) = pm_idle; + +#ifdef CONFIG_HOTPLUG_CPU + if (cpu_is_offline(smp_processor_id())) { + leds_event(led_idle_start); + cpu_die(); + } +#endif + if (!idle) idle = default_idle; preempt_disable(); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index b40915dcd533..edb5a406922f 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -159,6 +159,91 @@ int __cpuinit __cpu_up(unsigned int cpu) return ret; } +#ifdef CONFIG_HOTPLUG_CPU +/* + * __cpu_disable runs on the processor to be shutdown. + */ +int __cpuexit __cpu_disable(void) +{ + unsigned int cpu = smp_processor_id(); + struct task_struct *p; + int ret; + + ret = mach_cpu_disable(cpu); + if (ret) + return ret; + + /* + * Take this CPU offline. Once we clear this, we can't return, + * and we must not schedule until we're ready to give up the cpu. + */ + cpu_clear(cpu, cpu_online_map); + + /* + * OK - migrate IRQs away from this CPU + */ + migrate_irqs(); + + /* + * Flush user cache and TLB mappings, and then remove this CPU + * from the vm mask set of all processes. + */ + flush_cache_all(); + local_flush_tlb_all(); + + read_lock(&tasklist_lock); + for_each_process(p) { + if (p->mm) + cpu_clear(cpu, p->mm->cpu_vm_mask); + } + read_unlock(&tasklist_lock); + + return 0; +} + +/* + * called on the thread which is asking for a CPU to be shutdown - + * waits until shutdown has completed, or it is timed out. + */ +void __cpuexit __cpu_die(unsigned int cpu) +{ + if (!platform_cpu_kill(cpu)) + printk("CPU%u: unable to kill\n", cpu); +} + +/* + * Called from the idle thread for the CPU which has been shutdown. + * + * Note that we disable IRQs here, but do not re-enable them + * before returning to the caller. This is also the behaviour + * of the other hotplug-cpu capable cores, so presumably coming + * out of idle fixes this. + */ +void __cpuexit cpu_die(void) +{ + unsigned int cpu = smp_processor_id(); + + local_irq_disable(); + idle_task_exit(); + + /* + * actual CPU shutdown procedure is at least platform (if not + * CPU) specific + */ + platform_cpu_die(cpu); + + /* + * Do not return to the idle loop - jump back to the secondary + * cpu initialisation. There's some initialisation which needs + * to be repeated to undo the effects of taking the CPU offline. + */ + __asm__("mov sp, %0\n" + " b secondary_start_kernel" + : + : "r" ((void *)current->thread_info + THREAD_SIZE - 8)); +} +#endif /* CONFIG_HOTPLUG_CPU */ + /* * This is the secondary CPU boot entry. We're using this CPUs * idle thread stack, but a set of temporary page tables. diff --git a/include/asm-arm/irq.h b/include/asm-arm/irq.h index f97912fbb10f..59975ee43cf1 100644 --- a/include/asm-arm/irq.h +++ b/include/asm-arm/irq.h @@ -47,5 +47,6 @@ struct irqaction; struct pt_regs; int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); +extern void migrate_irqs(void); #endif diff --git a/include/asm-arm/smp.h b/include/asm-arm/smp.h index dbb4d859c586..551cd3c3093c 100644 --- a/include/asm-arm/smp.h +++ b/include/asm-arm/smp.h @@ -66,4 +66,14 @@ struct secondary_data { }; extern struct secondary_data secondary_data; +extern int __cpu_disable(void); +extern int mach_cpu_disable(unsigned int cpu); + +extern void __cpu_die(unsigned int cpu); +extern void cpu_die(void); + +extern void platform_cpu_die(unsigned int cpu); +extern int platform_cpu_kill(unsigned int cpu); +extern void platform_cpu_enable(unsigned int cpu); + #endif /* ifndef __ASM_ARM_SMP_H */ -- cgit v1.2.3-58-ga151