Name: Hot-plug CPU Boot Rewrite for PPC Author: Rusty Russell Status: Tested on 2.5.15/PPC Depends: Hotcpu/nonlinear-cpus-ppc.patch.gz Hotcpu/do-fork.patch.gz Hotcpu/daemonize.patch.gz D: This modifies the PPC boot sequence to "plug in" CPUs one at a D: time. diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/arch/ppc/kernel/open_pic.c working-ppc-2.5-hotcpu/arch/ppc/kernel/open_pic.c --- working-ppc-2.5-pre-hotcpu/arch/ppc/kernel/open_pic.c Thu May 23 18:23:50 2002 +++ working-ppc-2.5-hotcpu/arch/ppc/kernel/open_pic.c Mon May 27 15:03:53 2002 @@ -603,7 +603,7 @@ * -- 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()]; diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/arch/ppc/kernel/smp.c working-ppc-2.5-hotcpu/arch/ppc/kernel/smp.c --- working-ppc-2.5-pre-hotcpu/arch/ppc/kernel/smp.c Thu May 23 18:23:00 2002 +++ working-ppc-2.5-hotcpu/arch/ppc/kernel/smp.c Wed May 29 14:05:44 2002 @@ -48,15 +48,17 @@ 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 @@ 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 @@ atomic_inc(&call_data->finished); } +#if 0 /* Old boot code. */ void __init smp_boot_cpus(void) { int i, cpu_nr; @@ -558,3 +552,156 @@ } __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(®s, 0, sizeof(struct pt_regs)); + p = do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 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 diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/chrp_smp.c working-ppc-2.5-hotcpu/arch/ppc/platforms/chrp_smp.c --- working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/chrp_smp.c Thu May 23 16:04:30 2002 +++ working-ppc-2.5-hotcpu/arch/ppc/platforms/chrp_smp.c Wed May 29 14:05:25 2002 @@ -50,59 +50,61 @@ 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; - - /* 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 */ - 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 */ - 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(); } +static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED; +static unsigned int timebase_upper = 0, timebase_lower = 0; + +void __devinit +smp_chrp_give_timebase(void) +{ + spin_lock(&timebase_lock); + call_rtas("freeze-time-base", 0, 1, NULL); + 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); +} + +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, }; diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/gemini_setup.c working-ppc-2.5-hotcpu/arch/ppc/platforms/gemini_setup.c --- working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/gemini_setup.c Sun Feb 10 22:41:25 2002 +++ working-ppc-2.5-hotcpu/arch/ppc/platforms/gemini_setup.c Mon May 27 15:05:50 2002 @@ -527,6 +527,8 @@ 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 */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/iSeries_smp.c working-ppc-2.5-hotcpu/arch/ppc/platforms/iSeries_smp.c --- working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/iSeries_smp.c Thu May 23 16:05:09 2002 +++ working-ppc-2.5-hotcpu/arch/ppc/platforms/iSeries_smp.c Mon May 27 15:23:12 2002 @@ -117,7 +117,7 @@ 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 @@ 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, }; diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/pmac_smp.c working-ppc-2.5-hotcpu/arch/ppc/platforms/pmac_smp.c --- working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/pmac_smp.c Thu May 23 16:06:11 2002 +++ working-ppc-2.5-hotcpu/arch/ppc/platforms/pmac_smp.c Mon May 27 15:23:12 2002 @@ -612,6 +612,8 @@ 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 @@ smp_core99_probe, smp_core99_kick_cpu, smp_core99_setup_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, }; diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/pplus_setup.c working-ppc-2.5-hotcpu/arch/ppc/platforms/pplus_setup.c --- working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/pplus_setup.c Mon May 13 08:54:21 2002 +++ working-ppc-2.5-hotcpu/arch/ppc/platforms/pplus_setup.c Mon May 27 15:23:11 2002 @@ -318,6 +318,8 @@ 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 */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/prep_setup.c working-ppc-2.5-hotcpu/arch/ppc/platforms/prep_setup.c --- working-ppc-2.5-pre-hotcpu/arch/ppc/platforms/prep_setup.c Mon May 13 08:54:21 2002 +++ working-ppc-2.5-hotcpu/arch/ppc/platforms/prep_setup.c Mon May 27 16:36:25 2002 @@ -765,6 +765,8 @@ 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 */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/include/asm-ppc/machdep.h working-ppc-2.5-hotcpu/include/asm-ppc/machdep.h --- working-ppc-2.5-pre-hotcpu/include/asm-ppc/machdep.h Tue May 28 11:12:45 2002 +++ working-ppc-2.5-hotcpu/include/asm-ppc/machdep.h Tue May 28 16:47:06 2002 @@ -6,6 +6,7 @@ #define _PPC_MACHDEP_H #include +#include #ifdef CONFIG_APUS #include @@ -135,7 +136,14 @@ 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 */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal working-ppc-2.5-pre-hotcpu/include/asm-ppc/smp.h working-ppc-2.5-hotcpu/include/asm-ppc/smp.h --- working-ppc-2.5-pre-hotcpu/include/asm-ppc/smp.h Thu May 23 18:03:18 2002 +++ working-ppc-2.5-hotcpu/include/asm-ppc/smp.h Tue May 28 16:47:01 2002 @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef CONFIG_SMP @@ -31,11 +32,11 @@ 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 @@ #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) { @@ -61,6 +63,18 @@ return -1; } + +/* Upping and downing of CPUs */ +static inline int __cpu_disable(void) +{ + return -ENOSYS; +} + +static inline void __cpu_die(unsigned int cpu) +{ +} + +extern int __cpu_up(unsigned int cpu); extern int smp_hw_index[]; #define hard_smp_processor_id() (smp_hw_index[smp_processor_id()])