Commit 54b41b97 authored by Paul Mundt's avatar Paul Mundt

Merge branch 'sh/smp'

parents e60692b9 e7dc951e
......@@ -706,6 +706,13 @@ config NR_CPUS
This is purely to save memory - each supported CPU adds
approximately eight kilobytes to the kernel image.
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.
source "kernel/Kconfig.preempt"
config GUSA
......
......@@ -24,6 +24,7 @@
#include <cpu/sh7786.h>
#include <asm/heartbeat.h>
#include <asm/sizes.h>
#include <asm/smp-ops.h>
/*
* bit 1234 5678
......@@ -203,6 +204,8 @@ static void __init urquell_setup(char **cmdline_p)
printk(KERN_INFO "Renesas Technology Corp. Urquell support.\n");
pm_power_off = urquell_power_off;
register_smp_ops(&shx3_smp_ops);
}
/*
......
......@@ -21,6 +21,7 @@
#include <asm/heartbeat.h>
#include <asm/sizes.h>
#include <asm/reboot.h>
#include <asm/smp-ops.h>
static struct resource heartbeat_resource = {
.start = 0x07fff8b0,
......@@ -189,6 +190,8 @@ static void __init sdk7786_setup(char **cmdline_p)
machine_ops.restart = sdk7786_restart;
pm_power_off = sdk7786_power_off;
register_smp_ops(&shx3_smp_ops);
}
/*
......
......@@ -19,6 +19,7 @@
#include <linux/usb/r8a66597.h>
#include <linux/usb/m66592.h>
#include <asm/ilsel.h>
#include <asm/smp-ops.h>
static struct resource heartbeat_resources[] = {
[0] = {
......@@ -152,7 +153,13 @@ static void __init x3proto_init_irq(void)
__raw_writel(__raw_readl(0xfe410000) | (1 << 21), 0xfe410000);
}
static void __init x3proto_setup(char **cmdline_p)
{
register_smp_ops(&shx3_smp_ops);
}
static struct sh_machine_vector mv_x3proto __initmv = {
.mv_name = "x3proto",
.mv_setup = x3proto_setup,
.mv_init_irq = x3proto_init_irq,
};
#ifndef __ASM_SH_IRQ_H
#define __ASM_SH_IRQ_H
#include <linux/cpumask.h>
#include <asm/machvec.h>
/*
......@@ -50,6 +51,8 @@ static inline int generic_irq_demux(int irq)
#define irq_demux(irq) sh_mv.mv_irq_demux(irq)
void init_IRQ(void);
void migrate_irqs(void);
asmlinkage int do_IRQ(unsigned int irq, struct pt_regs *regs);
#ifdef CONFIG_IRQSTACKS
......
......@@ -85,6 +85,10 @@ struct sh_cpuinfo {
struct tlb_info itlb;
struct tlb_info dtlb;
#ifdef CONFIG_SMP
struct task_struct *idle;
#endif
unsigned long flags;
} __attribute__ ((aligned(L1_CACHE_BYTES)));
......
#ifndef __ASM_SH_SMP_OPS_H
#define __ASM_SH_SMP_OPS_H
struct plat_smp_ops {
void (*smp_setup)(void);
unsigned int (*smp_processor_id)(void);
void (*prepare_cpus)(unsigned int max_cpus);
void (*start_cpu)(unsigned int cpu, unsigned long entry_point);
void (*send_ipi)(unsigned int cpu, unsigned int message);
int (*cpu_disable)(unsigned int cpu);
void (*cpu_die)(unsigned int cpu);
void (*play_dead)(void);
};
extern struct plat_smp_ops *mp_ops;
extern struct plat_smp_ops shx3_smp_ops;
#ifdef CONFIG_SMP
static inline void plat_smp_setup(void)
{
BUG_ON(!mp_ops);
mp_ops->smp_setup();
}
static inline void play_dead(void)
{
mp_ops->play_dead();
}
extern void register_smp_ops(struct plat_smp_ops *ops);
#else
static inline void plat_smp_setup(void)
{
/* UP, nothing to do ... */
}
static inline void register_smp_ops(struct plat_smp_ops *ops)
{
}
static inline void play_dead(void)
{
BUG();
}
#endif /* CONFIG_SMP */
#endif /* __ASM_SH_SMP_OPS_H */
......@@ -3,15 +3,16 @@
#include <linux/bitops.h>
#include <linux/cpumask.h>
#include <asm/smp-ops.h>
#ifdef CONFIG_SMP
#include <linux/spinlock.h>
#include <asm/atomic.h>
#include <asm/current.h>
#include <asm/percpu.h>
#define raw_smp_processor_id() (current_thread_info()->cpu)
#define hard_smp_processor_id() plat_smp_processor_id()
/* Map from cpu id to sequential logical cpu number. */
extern int __cpu_number_map[NR_CPUS];
......@@ -30,20 +31,43 @@ enum {
SMP_MSG_NR, /* must be last */
};
DECLARE_PER_CPU(int, cpu_state);
void smp_message_recv(unsigned int msg);
void smp_timer_broadcast(const struct cpumask *mask);
void local_timer_interrupt(void);
void local_timer_setup(unsigned int cpu);
void plat_smp_setup(void);
void plat_prepare_cpus(unsigned int max_cpus);
int plat_smp_processor_id(void);
void plat_start_cpu(unsigned int cpu, unsigned long entry_point);
void plat_send_ipi(unsigned int cpu, unsigned int message);
void local_timer_stop(unsigned int cpu);
void arch_send_call_function_single_ipi(int cpu);
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
void arch_send_call_function_ipi_mask(const struct cpumask *mask);
void native_play_dead(void);
void native_cpu_die(unsigned int cpu);
int native_cpu_disable(unsigned int cpu);
#ifdef CONFIG_HOTPLUG_CPU
void play_dead_common(void);
extern int __cpu_disable(void);
static inline void __cpu_die(unsigned int cpu)
{
extern struct plat_smp_ops *mp_ops; /* private */
mp_ops->cpu_die(cpu);
}
#endif
static inline int hard_smp_processor_id(void)
{
extern struct plat_smp_ops *mp_ops; /* private */
if (!mp_ops)
return 0; /* boot CPU */
return mp_ops->smp_processor_id();
}
#else
......
/*
* SH-X3 SMP
*
* Copyright (C) 2007 - 2008 Paul Mundt
* Copyright (C) 2007 - 2010 Paul Mundt
* Copyright (C) 2007 Magnus Damm
*
* This file is subject to the terms and conditions of the GNU General Public
......@@ -9,16 +9,22 @@
* for more details.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/cpumask.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/cpu.h>
#include <asm/sections.h>
#define STBCR_REG(phys_id) (0xfe400004 | (phys_id << 12))
#define RESET_REG(phys_id) (0xfe400008 | (phys_id << 12))
#define STBCR_MSTP 0x00000001
#define STBCR_RESET 0x00000002
#define STBCR_SLEEP 0x00000004
#define STBCR_LTSLP 0x80000000
static irqreturn_t ipi_interrupt_handler(int irq, void *arg)
......@@ -37,7 +43,7 @@ static irqreturn_t ipi_interrupt_handler(int irq, void *arg)
return IRQ_HANDLED;
}
void __init plat_smp_setup(void)
static void shx3_smp_setup(void)
{
unsigned int cpu = 0;
int i, num;
......@@ -63,7 +69,7 @@ void __init plat_smp_setup(void)
printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num);
}
void __init plat_prepare_cpus(unsigned int max_cpus)
static void shx3_prepare_cpus(unsigned int max_cpus)
{
int i;
......@@ -74,9 +80,12 @@ void __init plat_prepare_cpus(unsigned int max_cpus)
for (i = 0; i < SMP_MSG_NR; i++)
request_irq(104 + i, ipi_interrupt_handler,
IRQF_DISABLED | IRQF_PERCPU, "IPI", (void *)(long)i);
for (i = 0; i < max_cpus; i++)
set_cpu_present(i, true);
}
void plat_start_cpu(unsigned int cpu, unsigned long entry_point)
static void shx3_start_cpu(unsigned int cpu, unsigned long entry_point)
{
if (__in_29bit_mode())
__raw_writel(entry_point, RESET_REG(cpu));
......@@ -93,12 +102,12 @@ void plat_start_cpu(unsigned int cpu, unsigned long entry_point)
__raw_writel(STBCR_RESET | STBCR_LTSLP, STBCR_REG(cpu));
}
int plat_smp_processor_id(void)
static unsigned int shx3_smp_processor_id(void)
{
return __raw_readl(0xff000048); /* CPIDR */
}
void plat_send_ipi(unsigned int cpu, unsigned int message)
static void shx3_send_ipi(unsigned int cpu, unsigned int message)
{
unsigned long addr = 0xfe410070 + (cpu * 4);
......@@ -106,3 +115,52 @@ void plat_send_ipi(unsigned int cpu, unsigned int message)
__raw_writel(1 << (message << 2), addr); /* C0INTICI..CnINTICI */
}
static void shx3_update_boot_vector(unsigned int cpu)
{
__raw_writel(STBCR_MSTP, STBCR_REG(cpu));
while (!(__raw_readl(STBCR_REG(cpu)) & STBCR_MSTP))
cpu_relax();
__raw_writel(STBCR_RESET, STBCR_REG(cpu));
}
static int __cpuinit
shx3_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned int)hcpu;
switch (action) {
case CPU_UP_PREPARE:
shx3_update_boot_vector(cpu);
break;
case CPU_ONLINE:
pr_info("CPU %u is now online\n", cpu);
break;
case CPU_DEAD:
break;
}
return NOTIFY_OK;
}
static struct notifier_block __cpuinitdata shx3_cpu_notifier = {
.notifier_call = shx3_cpu_callback,
};
static int __cpuinit register_shx3_cpu_notifier(void)
{
register_hotcpu_notifier(&shx3_cpu_notifier);
return 0;
}
late_initcall(register_shx3_cpu_notifier);
struct plat_smp_ops shx3_smp_ops = {
.smp_setup = shx3_smp_setup,
.prepare_cpus = shx3_prepare_cpus,
.start_cpu = shx3_start_cpu,
.smp_processor_id = shx3_smp_processor_id,
.send_ipi = shx3_send_ipi,
.cpu_die = native_cpu_die,
.cpu_disable = native_cpu_disable,
.play_dead = native_play_dead,
};
......@@ -19,6 +19,7 @@
#include <asm/pgalloc.h>
#include <asm/system.h>
#include <asm/atomic.h>
#include <asm/smp.h>
void (*pm_idle)(void) = NULL;
......@@ -89,10 +90,13 @@ void cpu_idle(void)
while (1) {
tick_nohz_stop_sched_tick(1);
while (!need_resched() && cpu_online(cpu)) {
while (!need_resched()) {
check_pgt_cache();
rmb();
if (cpu_is_offline(cpu))
play_dead();
local_irq_disable();
/* Don't trace irqs off for idle */
stop_critical_timings();
......@@ -133,7 +137,7 @@ static void do_nothing(void *unused)
void stop_this_cpu(void *unused)
{
local_irq_disable();
cpu_clear(smp_processor_id(), cpu_online_map);
set_cpu_online(smp_processor_id(), false);
for (;;)
cpu_sleep();
......
......@@ -12,6 +12,7 @@
#include <linux/kernel_stat.h>
#include <linux/seq_file.h>
#include <linux/ftrace.h>
#include <linux/delay.h>
#include <asm/processor.h>
#include <asm/machvec.h>
#include <asm/uaccess.h>
......@@ -292,3 +293,44 @@ int __init arch_probe_nr_irqs(void)
return 0;
}
#endif
#ifdef CONFIG_HOTPLUG_CPU
static void route_irq(struct irq_desc *desc, unsigned int irq, unsigned int cpu)
{
printk(KERN_INFO "IRQ%u: moving from cpu%u to cpu%u\n",
irq, desc->node, cpu);
raw_spin_lock_irq(&desc->lock);
desc->chip->set_affinity(irq, cpumask_of(cpu));
raw_spin_unlock_irq(&desc->lock);
}
/*
* 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)
{
struct irq_desc *desc;
unsigned int irq, cpu = smp_processor_id();
for_each_irq_desc(irq, desc) {
if (desc->node == cpu) {
unsigned int newcpu = cpumask_any_and(desc->affinity,
cpu_online_mask);
if (newcpu >= nr_cpu_ids) {
if (printk_ratelimit())
printk(KERN_INFO "IRQ%u no longer affine to CPU%u\n",
irq, cpu);
cpumask_setall(desc->affinity);
newcpu = cpumask_any_and(desc->affinity,
cpu_online_mask);
}
route_irq(desc, irq, newcpu);
}
}
}
#endif
......@@ -44,7 +44,7 @@ static void dummy_timer_set_mode(enum clock_event_mode mode,
{
}
void __cpuinit local_timer_setup(unsigned int cpu)
void local_timer_setup(unsigned int cpu)
{
struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
......@@ -60,3 +60,7 @@ void __cpuinit local_timer_setup(unsigned int cpu)
clockevents_register_device(clk);
}
void local_timer_stop(unsigned int cpu)
{
}
......@@ -39,6 +39,7 @@
#include <asm/irq.h>
#include <asm/setup.h>
#include <asm/clock.h>
#include <asm/smp.h>
#include <asm/mmu_context.h>
/*
......@@ -459,9 +460,7 @@ void __init setup_arch(char **cmdline_p)
if (likely(sh_mv.mv_setup))
sh_mv.mv_setup(cmdline_p);
#ifdef CONFIG_SMP
plat_smp_setup();
#endif
}
/* processor boot mode configuration */
......
......@@ -3,7 +3,7 @@
*
* SMP support for the SuperH processors.
*
* Copyright (C) 2002 - 2008 Paul Mundt
* Copyright (C) 2002 - 2010 Paul Mundt
* Copyright (C) 2006 - 2007 Akio Idehara
*
* This file is subject to the terms and conditions of the GNU General Public
......@@ -31,7 +31,20 @@
int __cpu_number_map[NR_CPUS]; /* Map physical to logical */
int __cpu_logical_map[NR_CPUS]; /* Map logical to physical */
static inline void __init smp_store_cpu_info(unsigned int cpu)
struct plat_smp_ops *mp_ops = NULL;
/* State of each CPU */
DEFINE_PER_CPU(int, cpu_state) = { 0 };
void __cpuinit register_smp_ops(struct plat_smp_ops *ops)
{
if (mp_ops)
printk(KERN_WARNING "Overriding previously set SMP ops\n");
mp_ops = ops;
}
static inline void __cpuinit smp_store_cpu_info(unsigned int cpu)
{
struct sh_cpuinfo *c = cpu_data + cpu;
......@@ -46,14 +59,14 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
init_new_context(current, &init_mm);
current_thread_info()->cpu = cpu;
plat_prepare_cpus(max_cpus);
mp_ops->prepare_cpus(max_cpus);
#ifndef CONFIG_HOTPLUG_CPU
init_cpu_present(&cpu_possible_map);
#endif
}
void __devinit smp_prepare_boot_cpu(void)
void __init smp_prepare_boot_cpu(void)
{
unsigned int cpu = smp_processor_id();
......@@ -62,37 +75,137 @@ void __devinit smp_prepare_boot_cpu(void)
set_cpu_online(cpu, true);
set_cpu_possible(cpu, true);
per_cpu(cpu_state, cpu) = CPU_ONLINE;
}
#ifdef CONFIG_HOTPLUG_CPU
void native_cpu_die(unsigned int cpu)
{
unsigned int i;
for (i = 0; i < 10; i++) {
smp_rmb();
if (per_cpu(cpu_state, cpu) == CPU_DEAD) {
if (system_state == SYSTEM_RUNNING)
pr_info("CPU %u is now offline\n", cpu);
return;
}
msleep(100);
}
pr_err("CPU %u didn't die...\n", cpu);
}
int native_cpu_disable(unsigned int cpu)
{
return cpu == 0 ? -EPERM : 0;
}
void play_dead_common(void)
{
idle_task_exit();
irq_ctx_exit(raw_smp_processor_id());
mb();
__get_cpu_var(cpu_state) = CPU_DEAD;
local_irq_disable();
}
void native_play_dead(void)
{
play_dead_common();
}
int __cpu_disable(void)
{
unsigned int cpu = smp_processor_id();
struct task_struct *p;
int ret;
ret = mp_ops->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.
*/
set_cpu_online(cpu, false);
/*
* OK - migrate IRQs away from this CPU
*/
migrate_irqs();
/*
* Stop the local timer for this CPU.
*/
local_timer_stop(cpu);
/*
* 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)
cpumask_clear_cpu(cpu, mm_cpumask(p->mm));
read_unlock(&tasklist_lock);
return 0;
}
#else /* ... !CONFIG_HOTPLUG_CPU */
int native_cpu_disable(void)
{
return -ENOSYS;
}
void native_cpu_die(unsigned int cpu)
{
/* We said "no" in __cpu_disable */
BUG();
}
void native_play_dead(void)
{
BUG();
}
#endif
asmlinkage void __cpuinit start_secondary(void)
{
unsigned int cpu;
unsigned int cpu = smp_processor_id();
struct mm_struct *mm = &init_mm;
enable_mmu();
atomic_inc(&mm->mm_count);
atomic_inc(&mm->mm_users);
current->active_mm = mm;
BUG_ON(current->mm);
enter_lazy_tlb(mm, current);
local_flush_tlb_all();
per_cpu_trap_init();
preempt_disable();
notify_cpu_starting(smp_processor_id());
notify_cpu_starting(cpu);
local_irq_enable();
cpu = smp_processor_id();
/* Enable local timers */
local_timer_setup(cpu);
calibrate_delay();
smp_store_cpu_info(cpu);
cpu_set(cpu, cpu_online_map);
set_cpu_online(cpu, true);
per_cpu(cpu_state, cpu) = CPU_ONLINE;
cpu_idle();
}
......@@ -111,12 +224,19 @@ int __cpuinit __cpu_up(unsigned int cpu)
struct task_struct *tsk;
unsigned long timeout;
tsk = fork_idle(cpu);
if (IS_ERR(tsk)) {
printk(KERN_ERR "Failed forking idle task for cpu %d\n", cpu);
return PTR_ERR(tsk);
tsk = cpu_data[cpu].idle;
if (!tsk) {
tsk = fork_idle(cpu);
if (IS_ERR(tsk)) {
pr_err("Failed forking idle task for cpu %d\n", cpu);
return PTR_ERR(tsk);
}
cpu_data[cpu].idle = tsk;
}
per_cpu(cpu_state, cpu) = CPU_UP_PREPARE;
/* Fill in data in head.S for secondary cpus */
stack_start.sp = tsk->thread.sp;
stack_start.thread_info = tsk->stack;
......@@ -127,7 +247,7 @@ int __cpuinit __cpu_up(unsigned int cpu)
(unsigned long)&stack_start + sizeof(stack_start));
wmb();
plat_start_cpu(cpu, (unsigned long)_stext);
mp_ops->start_cpu(cpu, (unsigned long)_stext);
timeout = jiffies + HZ;
while (time_before(jiffies, timeout)) {
......@@ -135,6 +255,7 @@ int __cpuinit __cpu_up(unsigned int cpu)
break;
udelay(10);
barrier();
}
if (cpu_online(cpu))
......@@ -159,7 +280,7 @@ void __init smp_cpus_done(unsigned int max_cpus)
void smp_send_reschedule(int cpu)
{
plat_send_ipi(cpu, SMP_MSG_RESCHEDULE);
mp_ops->send_ipi(cpu, SMP_MSG_RESCHEDULE);
}
void smp_send_stop(void)
......@@ -172,12 +293,12 @@ void arch_send_call_function_ipi_mask(const struct cpumask *mask)
int cpu;
for_each_cpu(cpu, mask)
plat_send_ipi(cpu, SMP_MSG_FUNCTION);
mp_ops->send_ipi(cpu, SMP_MSG_FUNCTION);
}
void arch_send_call_function_single_ipi(int cpu)
{
plat_send_ipi(cpu, SMP_MSG_FUNCTION_SINGLE);
mp_ops->send_ipi(cpu, SMP_MSG_FUNCTION_SINGLE);
}
void smp_timer_broadcast(const struct cpumask *mask)
......@@ -185,7 +306,7 @@ void smp_timer_broadcast(const struct cpumask *mask)
int cpu;
for_each_cpu(cpu, mask)
plat_send_ipi(cpu, SMP_MSG_TIMER);
mp_ops->send_ipi(cpu, SMP_MSG_TIMER);
}
static void ipi_timer(void)
......@@ -249,7 +370,6 @@ static void flush_tlb_mm_ipi(void *mm)
* behalf of debugees, kswapd stealing pages from another process etc).
* Kanoj 07/00.
*/
void flush_tlb_mm(struct mm_struct *mm)
{
preempt_disable();
......
......@@ -52,7 +52,11 @@ static int __init topology_init(void)
#endif
for_each_present_cpu(i) {
ret = register_cpu(&per_cpu(cpu_devices, i), i);
struct cpu *c = &per_cpu(cpu_devices, i);
c->hotpluggable = 1;
ret = register_cpu(c, i);
if (unlikely(ret))
printk(KERN_WARNING "%s: register_cpu %d failed (%d)\n",
__func__, i, ret);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment