Commit d243f1a8 authored by Rusty Russell's avatar Rusty Russell Committed by Linus Torvalds

[PATCH] Hot-plug CPU Boot Rewrite for PPC

This modifies the PPC boot sequence to "plug in" CPUs one at a
time.
parent 58ce9508
......@@ -602,7 +602,7 @@ void openpic_request_IPIs(void)
* -- Cort
*/
void __init do_openpic_setup_cpu(void)
void __devinit do_openpic_setup_cpu(void)
{
int i;
u32 msk = 1 << smp_hw_index[smp_processor_id()];
......
......@@ -48,15 +48,17 @@ struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 };
atomic_t ipi_recv;
atomic_t ipi_sent;
spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
unsigned int prof_multiplier[NR_CPUS];
unsigned int prof_counter[NR_CPUS];
unsigned long cache_decay_ticks;
static int max_cpus __initdata = NR_CPUS;
unsigned long cpu_online_map;
unsigned int prof_multiplier[NR_CPUS] = { [1 ... NR_CPUS-1] = 1 };
unsigned int prof_counter[NR_CPUS] = { [1 ... NR_CPUS-1] = 1 };
unsigned long cache_decay_ticks = HZ/100;
unsigned long cpu_online_map = 1UL;
unsigned long cpu_possible_map = 1UL;
int smp_hw_index[NR_CPUS];
static struct smp_ops_t *smp_ops;
struct thread_info *secondary_ti;
/* SMP operations for this machine */
static struct smp_ops_t *smp_ops;
/* all cpu mappings are 1-1 -- Cort */
volatile unsigned long cpu_callin_map[NR_CPUS];
......@@ -70,10 +72,6 @@ void smp_call_function_interrupt(void);
static int __smp_call_function(void (*func) (void *info), void *info,
int wait, int target);
#ifdef CONFIG_PPC_ISERIES
extern void smp_iSeries_space_timers( unsigned nr );
#endif
/* Since OpenPIC has only 4 IPIs, we use slightly different message numbers.
*
* Make sure this matches openpic_request_IPIs in open_pic.c, or what shows up
......@@ -291,6 +289,7 @@ void smp_call_function_interrupt(void)
atomic_inc(&call_data->finished);
}
#if 0 /* Old boot code. */
void __init smp_boot_cpus(void)
{
int i, cpu_nr;
......@@ -556,3 +555,156 @@ static int __init maxcpus(char *str)
}
__setup("maxcpus=", maxcpus);
#else /* New boot code */
/* FIXME: Do this properly for all archs --RR */
static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED;
static unsigned int timebase_upper = 0, timebase_lower = 0;
void __devinit
smp_generic_give_timebase(void)
{
spin_lock(&timebase_lock);
do {
timebase_upper = get_tbu();
timebase_lower = get_tbl();
} while (timebase_upper != get_tbu());
spin_unlock(&timebase_lock);
while (timebase_upper || timebase_lower)
rmb();
}
void __devinit
smp_generic_take_timebase(void)
{
int done = 0;
while (!done) {
spin_lock(&timebase_lock);
if (timebase_upper || timebase_lower) {
set_tb(timebase_upper, timebase_lower);
timebase_upper = 0;
timebase_lower = 0;
done = 1;
}
spin_unlock(&timebase_lock);
}
}
static void __devinit smp_store_cpu_info(int id)
{
struct cpuinfo_PPC *c = &cpu_data[id];
/* assume bogomips are same for everything */
c->loops_per_jiffy = loops_per_jiffy;
c->pvr = mfspr(PVR);
}
void __init smp_prepare_cpus(unsigned int max_cpus)
{
int num_cpus;
/* Fixup boot cpu */
smp_store_cpu_info(smp_processor_id());
cpu_callin_map[smp_processor_id()] = 1;
smp_ops = ppc_md.smp_ops;
if (smp_ops == NULL) {
printk("SMP not supported on this machine.\n");
return;
}
/* Probe platform for CPUs: always linear. */
num_cpus = smp_ops->probe();
cpu_possible_map = (1 << num_cpus)-1;
if (smp_ops->space_timers)
smp_ops->space_timers(num_cpus);
}
int __init setup_profiling_timer(unsigned int multiplier)
{
return 0;
}
/* Processor coming up starts here */
int __devinit start_secondary(void *unused)
{
int cpu;
atomic_inc(&init_mm.mm_count);
current->active_mm = &init_mm;
cpu = smp_processor_id();
smp_store_cpu_info(cpu);
set_dec(tb_ticks_per_jiffy);
cpu_callin_map[cpu] = 1;
printk("CPU %i done callin...\n", cpu);
smp_ops->setup_cpu(cpu);
printk("CPU %i done setup...\n", cpu);
smp_ops->take_timebase();
printk("CPU %i done timebase take...\n", cpu);
return cpu_idle(NULL);
}
int __cpu_up(unsigned int cpu)
{
struct pt_regs regs;
struct task_struct *p;
char buf[32];
int c;
/* create a process for the processor */
/* only regs.msr is actually used, and 0 is OK for it */
memset(&regs, 0, sizeof(struct pt_regs));
p = do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
if (IS_ERR(p))
panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p));
init_idle(p, cpu);
unhash_process(p);
secondary_ti = p->thread_info;
p->thread_info->cpu = cpu;
/*
* There was a cache flush loop here to flush the cache
* to memory for the first 8MB of RAM. The cache flush
* has been pushed into the kick_cpu function for those
* platforms that need it.
*/
/* wake up cpu */
smp_ops->kick_cpu(cpu);
/*
* wait to see if the cpu made a callin (is actually up).
* use this value that I found through experimentation.
* -- Cort
*/
for (c = 1000; c && !cpu_callin_map[cpu]; c--)
udelay(100);
if (!cpu_callin_map[cpu]) {
sprintf(buf, "didn't find cpu %u", cpu);
if (ppc_md.progress) ppc_md.progress(buf, 0x360+cpu);
printk("Processor %u is stuck.\n", cpu);
return -ENOENT;
}
sprintf(buf, "found cpu %u", cpu);
if (ppc_md.progress) ppc_md.progress(buf, 0x350+cpu);
printk("Processor %d found.\n", cpu);
smp_ops->give_timebase();
set_bit(cpu, &cpu_online_map);
return 0;
}
void smp_cpus_done(unsigned int max_cpus)
{
smp_ops->setup_cpu(0);
}
#endif
......@@ -50,59 +50,61 @@ smp_chrp_probe(void)
return smp_chrp_cpu_nr;
}
static void __init
static void __devinit
smp_chrp_kick_cpu(int nr)
{
*(unsigned long *)KERNELBASE = nr;
asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory");
}
static void __init
static void __devinit
smp_chrp_setup_cpu(int cpu_nr)
{
static atomic_t ready = ATOMIC_INIT(1);
static volatile int frozen = 0;
if (OpenPIC_Addr)
do_openpic_setup_cpu();
}
static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED;
static unsigned int timebase_upper = 0, timebase_lower = 0;
/* FIXME: Hotplug cpu breaks all this --RR */
if (cpu_nr == 0) {
/* wait for all the others */
while (atomic_read(&ready) < num_online_cpus())
barrier();
atomic_set(&ready, 1);
/* freeze the timebase */
void __devinit
smp_chrp_give_timebase(void)
{
spin_lock(&timebase_lock);
call_rtas("freeze-time-base", 0, 1, NULL);
mb();
frozen = 1;
/* XXX assumes this is not a 601 */
set_tb(0, 0);
last_jiffy_stamp(0) = 0;
while (atomic_read(&ready) < num_online_cpus())
barrier();
/* thaw the timebase again */
timebase_upper = get_tbu();
timebase_lower = get_tbl();
spin_unlock(&timebase_lock);
while (timebase_upper || timebase_lower)
rmb();
call_rtas("thaw-time-base", 0, 1, NULL);
mb();
frozen = 0;
smp_tb_synchronized = 1;
} else {
atomic_inc(&ready);
while (!frozen)
barrier();
set_tb(0, 0);
last_jiffy_stamp(0) = 0;
mb();
atomic_inc(&ready);
while (frozen)
barrier();
}
}
if (OpenPIC_Addr)
do_openpic_setup_cpu();
void __devinit
smp_chrp_take_timebase(void)
{
int done = 0;
while (!done) {
spin_lock(&timebase_lock);
if (timebase_upper || timebase_lower) {
set_tb(timebase_upper, timebase_lower);
timebase_upper = 0;
timebase_lower = 0;
done = 1;
}
spin_unlock(&timebase_lock);
}
printk("CPU %i taken timebase\n", smp_processor_id());
}
/* CHRP with openpic */
struct smp_ops_t chrp_smp_ops __chrpdata = {
smp_openpic_message_pass,
smp_chrp_probe,
smp_chrp_kick_cpu,
smp_chrp_setup_cpu,
.message_pass = smp_openpic_message_pass,
.probe = smp_chrp_probe,
.kick_cpu = smp_chrp_kick_cpu,
.setup_cpu = smp_chrp_setup_cpu,
.give_timebase = smp_chrp_give_timebase,
.take_timebase = smp_chrp_take_timebase,
};
......@@ -528,6 +528,8 @@ static struct smp_ops_t gemini_smp_ops = {
smp_gemini_probe,
smp_gemini_kick_cpu,
smp_gemini_setup_cpu,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
};
#endif /* CONFIG_SMP */
......
......@@ -117,7 +117,7 @@ static void smp_iSeries_setup_cpu(int nr)
set_dec( xPaca[nr].default_decr );
}
void smp_iSeries_space_timers( unsigned nr )
static void smp_iSeries_space_timers(unsigned nr)
{
unsigned offset,i;
......@@ -131,6 +131,9 @@ struct smp_ops_t iSeries_smp_ops = {
smp_iSeries_message_pass,
smp_iSeries_probe,
smp_iSeries_kick_cpu,
smp_iSeries_setup_cpu
smp_iSeries_setup_cpu,
smp_iSeries_space_timers,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
};
......@@ -612,6 +612,8 @@ struct smp_ops_t psurge_smp_ops __pmacdata = {
smp_psurge_probe,
smp_psurge_kick_cpu,
smp_psurge_setup_cpu,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
};
/* Core99 Macs (dual G4s) */
......@@ -620,4 +622,6 @@ struct smp_ops_t core99_smp_ops __pmacdata = {
smp_core99_probe,
smp_core99_kick_cpu,
smp_core99_setup_cpu,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
};
......@@ -309,6 +309,8 @@ static struct smp_ops_t pplus_smp_ops = {
smp_pplus_probe,
smp_pplus_kick_cpu,
smp_pplus_setup_cpu,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
};
#endif /* CONFIG_SMP */
......
......@@ -756,6 +756,8 @@ static struct smp_ops_t prep_smp_ops __prepdata = {
smp_prep_probe,
smp_prep_kick_cpu,
smp_prep_setup_cpu,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
};
#endif /* CONFIG_SMP */
......
......@@ -6,6 +6,7 @@
#define _PPC_MACHDEP_H
#include <linux/config.h>
#include <linux/init.h>
#ifdef CONFIG_APUS
#include <asm-m68k/machdep.h>
......@@ -129,7 +130,14 @@ struct smp_ops_t {
int (*probe)(void);
void (*kick_cpu)(int nr);
void (*setup_cpu)(int nr);
void (*space_timers)(int nr);
void (*take_timebase)(void);
void (*give_timebase)(void);
};
/* Poor default implementations */
extern void __devinit smp_generic_give_timebase(void);
extern void __devinit smp_generic_take_timebase(void);
#endif /* CONFIG_SMP */
#endif /* _PPC_MACHDEP_H */
......
......@@ -16,6 +16,7 @@
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#ifdef CONFIG_SMP
......@@ -31,11 +32,11 @@ struct cpuinfo_PPC {
extern struct cpuinfo_PPC cpu_data[];
extern unsigned long cpu_online_map;
extern unsigned long cpu_possible_map;
extern unsigned long smp_proc_in_lock[];
extern volatile unsigned long cpu_callin_map[];
extern int smp_tb_synchronized;
extern void smp_store_cpu_info(int id);
extern void smp_send_tlb_invalidate(int);
extern void smp_send_xmon_break(int cpu);
struct pt_regs;
......@@ -48,6 +49,7 @@ extern void smp_local_timer_interrupt(struct pt_regs *);
#define smp_processor_id() (current_thread_info()->cpu)
#define cpu_online(cpu) (cpu_online_map & (1<<(cpu)))
#define cpu_possible(cpu) (cpu_possible_map & (1<<(cpu)))
extern inline unsigned int num_online_cpus(void)
{
......@@ -62,6 +64,8 @@ extern inline int any_online_cpu(unsigned int mask)
return -1;
}
extern int __cpu_up(unsigned int cpu);
extern int smp_hw_index[];
#define hard_smp_processor_id() (smp_hw_index[smp_processor_id()])
......
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