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) ...@@ -602,7 +602,7 @@ void openpic_request_IPIs(void)
* -- Cort * -- Cort
*/ */
void __init do_openpic_setup_cpu(void) void __devinit do_openpic_setup_cpu(void)
{ {
int i; int i;
u32 msk = 1 << smp_hw_index[smp_processor_id()]; u32 msk = 1 << smp_hw_index[smp_processor_id()];
......
...@@ -48,15 +48,17 @@ struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 }; ...@@ -48,15 +48,17 @@ struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 };
atomic_t ipi_recv; atomic_t ipi_recv;
atomic_t ipi_sent; atomic_t ipi_sent;
spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
unsigned int prof_multiplier[NR_CPUS]; unsigned int prof_multiplier[NR_CPUS] = { [1 ... NR_CPUS-1] = 1 };
unsigned int prof_counter[NR_CPUS]; unsigned int prof_counter[NR_CPUS] = { [1 ... NR_CPUS-1] = 1 };
unsigned long cache_decay_ticks; unsigned long cache_decay_ticks = HZ/100;
static int max_cpus __initdata = NR_CPUS; unsigned long cpu_online_map = 1UL;
unsigned long cpu_online_map; unsigned long cpu_possible_map = 1UL;
int smp_hw_index[NR_CPUS]; int smp_hw_index[NR_CPUS];
static struct smp_ops_t *smp_ops;
struct thread_info *secondary_ti; struct thread_info *secondary_ti;
/* SMP operations for this machine */
static struct smp_ops_t *smp_ops;
/* all cpu mappings are 1-1 -- Cort */ /* all cpu mappings are 1-1 -- Cort */
volatile unsigned long cpu_callin_map[NR_CPUS]; volatile unsigned long cpu_callin_map[NR_CPUS];
...@@ -70,10 +72,6 @@ void smp_call_function_interrupt(void); ...@@ -70,10 +72,6 @@ void smp_call_function_interrupt(void);
static int __smp_call_function(void (*func) (void *info), void *info, static int __smp_call_function(void (*func) (void *info), void *info,
int wait, int target); 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. /* 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 * 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) ...@@ -291,6 +289,7 @@ void smp_call_function_interrupt(void)
atomic_inc(&call_data->finished); atomic_inc(&call_data->finished);
} }
#if 0 /* Old boot code. */
void __init smp_boot_cpus(void) void __init smp_boot_cpus(void)
{ {
int i, cpu_nr; int i, cpu_nr;
...@@ -556,3 +555,156 @@ static int __init maxcpus(char *str) ...@@ -556,3 +555,156 @@ static int __init maxcpus(char *str)
} }
__setup("maxcpus=", maxcpus); __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) ...@@ -50,59 +50,61 @@ smp_chrp_probe(void)
return smp_chrp_cpu_nr; return smp_chrp_cpu_nr;
} }
static void __init static void __devinit
smp_chrp_kick_cpu(int nr) smp_chrp_kick_cpu(int nr)
{ {
*(unsigned long *)KERNELBASE = nr; *(unsigned long *)KERNELBASE = nr;
asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory");
} }
static void __init static void __devinit
smp_chrp_setup_cpu(int cpu_nr) smp_chrp_setup_cpu(int cpu_nr)
{ {
static atomic_t ready = ATOMIC_INIT(1); if (OpenPIC_Addr)
static volatile int frozen = 0; 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 */ void __devinit
if (cpu_nr == 0) { smp_chrp_give_timebase(void)
/* wait for all the others */ {
while (atomic_read(&ready) < num_online_cpus()) spin_lock(&timebase_lock);
barrier();
atomic_set(&ready, 1);
/* freeze the timebase */
call_rtas("freeze-time-base", 0, 1, NULL); call_rtas("freeze-time-base", 0, 1, NULL);
mb(); timebase_upper = get_tbu();
frozen = 1; timebase_lower = get_tbl();
/* XXX assumes this is not a 601 */ spin_unlock(&timebase_lock);
set_tb(0, 0);
last_jiffy_stamp(0) = 0; while (timebase_upper || timebase_lower)
while (atomic_read(&ready) < num_online_cpus()) rmb();
barrier();
/* thaw the timebase again */
call_rtas("thaw-time-base", 0, 1, NULL); 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) void __devinit
do_openpic_setup_cpu(); 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 */ /* CHRP with openpic */
struct smp_ops_t chrp_smp_ops __chrpdata = { struct smp_ops_t chrp_smp_ops __chrpdata = {
smp_openpic_message_pass, .message_pass = smp_openpic_message_pass,
smp_chrp_probe, .probe = smp_chrp_probe,
smp_chrp_kick_cpu, .kick_cpu = smp_chrp_kick_cpu,
smp_chrp_setup_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 = { ...@@ -528,6 +528,8 @@ static struct smp_ops_t gemini_smp_ops = {
smp_gemini_probe, smp_gemini_probe,
smp_gemini_kick_cpu, smp_gemini_kick_cpu,
smp_gemini_setup_cpu, smp_gemini_setup_cpu,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
}; };
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
......
...@@ -117,7 +117,7 @@ static void smp_iSeries_setup_cpu(int nr) ...@@ -117,7 +117,7 @@ static void smp_iSeries_setup_cpu(int nr)
set_dec( xPaca[nr].default_decr ); set_dec( xPaca[nr].default_decr );
} }
void smp_iSeries_space_timers( unsigned nr ) static void smp_iSeries_space_timers(unsigned nr)
{ {
unsigned offset,i; unsigned offset,i;
...@@ -131,6 +131,9 @@ struct smp_ops_t iSeries_smp_ops = { ...@@ -131,6 +131,9 @@ struct smp_ops_t iSeries_smp_ops = {
smp_iSeries_message_pass, smp_iSeries_message_pass,
smp_iSeries_probe, smp_iSeries_probe,
smp_iSeries_kick_cpu, 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 = { ...@@ -612,6 +612,8 @@ struct smp_ops_t psurge_smp_ops __pmacdata = {
smp_psurge_probe, smp_psurge_probe,
smp_psurge_kick_cpu, smp_psurge_kick_cpu,
smp_psurge_setup_cpu, smp_psurge_setup_cpu,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
}; };
/* Core99 Macs (dual G4s) */ /* Core99 Macs (dual G4s) */
...@@ -620,4 +622,6 @@ struct smp_ops_t core99_smp_ops __pmacdata = { ...@@ -620,4 +622,6 @@ struct smp_ops_t core99_smp_ops __pmacdata = {
smp_core99_probe, smp_core99_probe,
smp_core99_kick_cpu, smp_core99_kick_cpu,
smp_core99_setup_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 = { ...@@ -309,6 +309,8 @@ static struct smp_ops_t pplus_smp_ops = {
smp_pplus_probe, smp_pplus_probe,
smp_pplus_kick_cpu, smp_pplus_kick_cpu,
smp_pplus_setup_cpu, smp_pplus_setup_cpu,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
}; };
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
......
...@@ -756,6 +756,8 @@ static struct smp_ops_t prep_smp_ops __prepdata = { ...@@ -756,6 +756,8 @@ static struct smp_ops_t prep_smp_ops __prepdata = {
smp_prep_probe, smp_prep_probe,
smp_prep_kick_cpu, smp_prep_kick_cpu,
smp_prep_setup_cpu, smp_prep_setup_cpu,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
}; };
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define _PPC_MACHDEP_H #define _PPC_MACHDEP_H
#include <linux/config.h> #include <linux/config.h>
#include <linux/init.h>
#ifdef CONFIG_APUS #ifdef CONFIG_APUS
#include <asm-m68k/machdep.h> #include <asm-m68k/machdep.h>
...@@ -129,7 +130,14 @@ struct smp_ops_t { ...@@ -129,7 +130,14 @@ struct smp_ops_t {
int (*probe)(void); int (*probe)(void);
void (*kick_cpu)(int nr); void (*kick_cpu)(int nr);
void (*setup_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 /* CONFIG_SMP */
#endif /* _PPC_MACHDEP_H */ #endif /* _PPC_MACHDEP_H */
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/errno.h>
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
...@@ -31,11 +32,11 @@ struct cpuinfo_PPC { ...@@ -31,11 +32,11 @@ struct cpuinfo_PPC {
extern struct cpuinfo_PPC cpu_data[]; extern struct cpuinfo_PPC cpu_data[];
extern unsigned long cpu_online_map; extern unsigned long cpu_online_map;
extern unsigned long cpu_possible_map;
extern unsigned long smp_proc_in_lock[]; extern unsigned long smp_proc_in_lock[];
extern volatile unsigned long cpu_callin_map[]; extern volatile unsigned long cpu_callin_map[];
extern int smp_tb_synchronized; extern int smp_tb_synchronized;
extern void smp_store_cpu_info(int id);
extern void smp_send_tlb_invalidate(int); extern void smp_send_tlb_invalidate(int);
extern void smp_send_xmon_break(int cpu); extern void smp_send_xmon_break(int cpu);
struct pt_regs; struct pt_regs;
...@@ -48,6 +49,7 @@ extern void smp_local_timer_interrupt(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 smp_processor_id() (current_thread_info()->cpu)
#define cpu_online(cpu) (cpu_online_map & (1<<(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) extern inline unsigned int num_online_cpus(void)
{ {
...@@ -62,6 +64,8 @@ extern inline int any_online_cpu(unsigned int mask) ...@@ -62,6 +64,8 @@ extern inline int any_online_cpu(unsigned int mask)
return -1; return -1;
} }
extern int __cpu_up(unsigned int cpu);
extern int smp_hw_index[]; extern int smp_hw_index[];
#define hard_smp_processor_id() (smp_hw_index[smp_processor_id()]) #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