ppc64: cleanup/split SMP code
PATCH 1/1 ppc64: cleanup/split SMP code
Hi !
This patch depends at least on two previously posted ones (and not yet merged).
[PATCH] ppc64: Fix pSeries secondary CPU setup
[PATCH] ppc64: Rewrite the openpic driver
Splits arch/ppc64/kernel/smp.c into 3 different files, smp.c, pSeries_smp.c and iSeries_smp.c, thus removing most of the #define mess in those files and making it easier to add a new platform.
Signed-off-by Benjamin Herrenschmidt <[email protected]>
arch/ppc64/kernel/pSeries_smp.c new file +393 -0
@@ -0,0 +1,393 @@ +/* +* SMP support for pSeries machines. +* +* Dave Engebretsen, Peter Bergner, and +* Mike Corrigan {engebret|bergner|mikec}@us.ibm.com +* +* Plus various changes from other IBM teams... +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version +* 2 of the License, or (at your option) any later version. +*/ + +#undef DEBUG + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/cache.h> +#include <linux/err.h> +#include <linux/sysdev.h> +#include <linux/cpu.h> + +#include <asm/ptrace.h> +#include <asm/atomic.h> +#include <asm/irq.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/smp.h> +#include <asm/naca.h> +#include <asm/paca.h> +#include <asm/time.h> +#include <asm/ppcdebug.h> +#include <asm/machdep.h> +#include <asm/xics.h> +#include <asm/cputable.h> +#include <asm/system.h> +#include <asm/rtas.h> +#include <asm/plpar_wrappers.h> + +#include "mpic.h" + +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +extern void pseries_secondary_smp_init (unsigned long ); + +static void vpa_init (int cpu) +{ +unsigned long flags, pcpu =get_hard_smp_processor_id (cpu); + +/* Register the Virtual Processor Area (VPA) */ + flags =1UL << (63 -18 ); +register_vpa (flags, pcpu,__pa ((unsigned long )&(paca[cpu].lppaca))); +} + + +/* Get state of physical CPU. +* Return codes: +* 0 - The processor is in the RTAS stopped state +* 1 - stop-self is in progress +* 2 - The processor is not in the RTAS stopped state +* -1 - Hardware Error +* -2 - Hardware Busy, Try again later. +*/ +static int query_cpu_stopped (unsigned int pcpu) +{ +int cpu_status; +int status, qcss_tok; + + qcss_tok =rtas_token ("query-cpu-stopped-state" ); +if (qcss_tok ==RTAS_UNKNOWN_SERVICE ) +return -1 ; + status =rtas_call (qcss_tok,1 ,2 , &cpu_status, pcpu); +if (status !=0 ) { +printk (KERN_ERR +"RTAS query-cpu-stopped-state failed: %i\n" , status); +return status; + } + +return cpu_status; +} + + +#ifdef CONFIG_HOTPLUG_CPU + +int __cpu_disable (void ) +{ +/* FIXME: go put this in a header somewhere */ +extern void xics_migrate_irqs_away (void ); + + systemcfg->processorCount--; + +/*fix boot_cpuid here*/ +if (smp_processor_id () == boot_cpuid) + boot_cpuid =any_online_cpu (cpu_online_map); + +/* FIXME: abstract this to not be platform specific later on */ +xics_migrate_irqs_away (); +return 0 ; +} + +void __cpu_die (unsigned int cpu) +{ +int tries; +int cpu_status; +unsigned int pcpu =get_hard_smp_processor_id (cpu); + +for (tries =0 ; tries <25 ; tries++) { + cpu_status =query_cpu_stopped (pcpu); +if (cpu_status ==0 || cpu_status ==-1 ) +break ; +set_current_state (TASK_UNINTERRUPTIBLE ); +schedule_timeout (HZ /5 ); + } +if (cpu_status !=0 ) { +printk ("Querying DEAD? cpu %i (%i) shows %i\n" , + cpu, pcpu, cpu_status); + } + +/* Isolation and deallocation are definatly done by +* drslot_chrp_cpu. If they were not they would be +* done here. Change isolate state to Isolate and +* change allocation-state to Unusable. +*/ + paca[cpu].cpu_start =0 ; +} + +/* Search all cpu device nodes for an offline logical cpu. If a +* device node has a "ibm,my-drc-index" property (meaning this is an +* LPAR), paranoid-check whether we own the cpu. For each "thread" +* of a cpu, if it is offline and has the same hw index as before, +* grab that in preference. +*/ +static unsigned int find_physical_cpu_to_start (unsigned int old_hwindex) +{ +struct device_node *np =NULL ; +unsigned int best =-1U ; + +while ((np =of_find_node_by_type (np,"cpu" ))) { +int nr_threads, len; +u32 *index = (u32 *)get_property (np,"ibm,my-drc-index" ,NULL ); +u32 *tid = (u32 *) +get_property (np,"ibm,ppc-interrupt-server#s" , &len); + +if (!tid) + tid = (u32 *)get_property (np,"reg" , &len); + +if (!tid) +continue ; + +/* If there is a drc-index, make sure that we own +* the cpu. +*/ +if (index) { +int state; +int rc =rtas_get_sensor (9003 , *index, &state); +if (rc !=0 || state !=1 ) +continue ; + } + + nr_threads = len /sizeof (u32); + +while (nr_threads--) { +if (0 ==query_cpu_stopped (tid[nr_threads])) { + best = tid[nr_threads]; +if (best == old_hwindex) + goto out; + } + } + } +out: +of_node_put (np); +return best; +} + +/** +* smp_startup_cpu() - start the given cpu +* +* At boot time, there is nothing to do. At run-time, call RTAS with +* the appropriate start location, if the cpu is in the RTAS stopped +* state. +* +* Returns: +* 0 - failure +* 1 - success +*/ +static inline int __devinit smp_startup_cpu (unsigned int lcpu) +{ +int status; +unsigned long start_here =__pa ((u32)*((unsigned long *) + pseries_secondary_smp_init)); +unsigned int pcpu; + +/* At boot time the cpus are already spinning in hold +* loops, so nothing to do. */ +if (system_state <SYSTEM_RUNNING ) +return 1 ; + + pcpu =find_physical_cpu_to_start (get_hard_smp_processor_id (lcpu)); +if (pcpu ==-1U ) { +printk (KERN_INFO "No more cpus available, failing\n" ); +return 0 ; + } + +/* Fixup atomic count: it exited inside IRQ handler. */ + paca[lcpu].__current->thread_info->preempt_count =0 ; + +/* At boot this is done in prom.c. */ + paca[lcpu].hw_cpu_id = pcpu; + + status =rtas_call (rtas_token ("start-cpu" ),3 ,1 ,NULL , + pcpu, start_here, lcpu); +if (status !=0 ) { +printk (KERN_ERR "start-cpu failed: %i\n" , status); +return 0 ; + } +return 1 ; +} +#else /* ... CONFIG_HOTPLUG_CPU */ +static inline int __devinit smp_startup_cpu (unsigned int lcpu) +{ +return 1 ; +} +#endif /* CONFIG_HOTPLUG_CPU */ + +static inline void smp_xics_do_message (int cpu,int msg) +{ +set_bit (msg, &xics_ipi_message[cpu].value); +mb (); +xics_cause_IPI (cpu); +} + +static void smp_xics_message_pass (int target,int msg) +{ +unsigned int i; + +if (target <NR_CPUS ) { +smp_xics_do_message (target, msg); + }else { +for_each_online_cpu (i) { +if (target ==MSG_ALL_BUT_SELF + && i ==smp_processor_id ()) +continue ; +smp_xics_do_message (i, msg); + } + } +} + +extern void xics_request_IPIs (void ); + +static int __init smp_xics_probe (void ) +{ +xics_request_IPIs (); + +return cpus_weight (cpu_possible_map); +} + +static void __devinit smp_xics_setup_cpu (int cpu) +{ +if (cpu != boot_cpuid) +xics_setup_cpu (); +} + +static spinlock_t timebase_lock =SPIN_LOCK_UNLOCKED ; +static unsigned long timebase =0 ; + +static void __devinit pSeries_give_timebase (void ) +{ +spin_lock (&timebase_lock); +rtas_call (rtas_token ("freeze-time-base" ),0 ,1 ,NULL ); + timebase =get_tb (); +spin_unlock (&timebase_lock); + +while (timebase) +barrier (); +rtas_call (rtas_token ("thaw-time-base" ),0 ,1 ,NULL ); +} + +static void __devinit pSeries_take_timebase (void ) +{ +while (!timebase) +barrier (); +spin_lock (&timebase_lock); +set_tb (timebase >>32 , timebase &0xffffffff ); + timebase =0 ; +spin_unlock (&timebase_lock); +} + +static void __devinit pSeries_late_setup_cpu (int cpu) +{ +extern unsigned int default_distrib_server; + +if (cur_cpu_spec->firmware_features &FW_FEATURE_SPLPAR ) { +vpa_init (cpu); + } + +#ifdef CONFIG_IRQ_ALL_CPUS +/* Put the calling processor into the GIQ. This is really only +* necessary from a secondary thread as the OF start-cpu interface +* performs this function for us on primary threads. +*/ +/* TODO: 9005 is #defined in rtas-proc.c -- move to a header */ +rtas_set_indicator (9005 , default_distrib_server,1 ); +#endif +} + + +void __devinit smp_pSeries_kick_cpu (int nr) +{ +BUG_ON (nr <0 || nr >=NR_CPUS ); + +if (!smp_startup_cpu (nr)) +return ; + +/* +* The processor is currently spinning, waiting for the +* cpu_start field to become non-zero After we set cpu_start, +* the processor will continue on to secondary_start +*/ + paca[nr].cpu_start =1 ; +} + +static struct smp_ops_t pSeries_mpic_smp_ops = { + .message_pass = smp_mpic_message_pass, + .probe = smp_mpic_probe, + .kick_cpu = smp_pSeries_kick_cpu, + .setup_cpu = smp_mpic_setup_cpu, + .late_setup_cpu = pSeries_late_setup_cpu, +}; + +static struct smp_ops_t pSeries_xics_smp_ops = { + .message_pass = smp_xics_message_pass, + .probe = smp_xics_probe, + .kick_cpu = smp_pSeries_kick_cpu, + .setup_cpu = smp_xics_setup_cpu, + .late_setup_cpu = pSeries_late_setup_cpu, +}; + +/* This is called very early */ +void __init smp_init_pSeries (void ) +{ +int ret, i; + +DBG (" -> smp_init_pSeries()\n" ); + +if (naca->interrupt_controller ==IC_OPEN_PIC ) + smp_ops = &pSeries_mpic_smp_ops; +else + smp_ops = &pSeries_xics_smp_ops; + +/* Start secondary threads on SMT systems; primary threads +* are already in the running state. +*/ +for_each_present_cpu (i) { +if (query_cpu_stopped (get_hard_smp_processor_id (i)) ==0 ) { +printk ("%16.16x : starting thread\n" , i); +DBG ("%16.16x : starting thread\n" , i); +rtas_call (rtas_token ("start-cpu" ),3 ,1 , &ret, +get_hard_smp_processor_id (i), +__pa ((u32)*((unsigned long *) + pseries_secondary_smp_init)), + i); + } + } + +if (cur_cpu_spec->firmware_features &FW_FEATURE_SPLPAR ) +vpa_init (boot_cpuid); + +/* Non-lpar has additional take/give timebase */ +if (systemcfg->platform ==PLATFORM_PSERIES ) { + smp_ops->give_timebase = pSeries_give_timebase; + smp_ops->take_timebase = pSeries_take_timebase; + } + + +DBG (" <- smp_init_pSeries()\n" ); +} +
arch/ppc64/kernel/smp.c +16 -452
539 lines not on this page · raw message
include/asm-ppc64/machdep.h +2 -0
@@ -28,6 +28,7 @@int (*probe)(void );void (*kick_cpu)(int nr);void (*setup_cpu)(int nr); +void (*late_setup_cpu)(int nr);void (*take_timebase)(void );void (*give_timebase)(void ); };
@@ -86,6 +87,7 @@void (*power_off)(void );void (*halt)(void );void (*panic)(char *str); +void (*cpu_die)(void );int (*set_rtc_time)(struct rtc_time *);void (*get_rtc_time)(struct rtc_time *);
include/asm-ppc64/smp.h +9 -3
@@ -28,6 +28,8 @@extern int boot_cpuid; +extern void cpu_die (void ) __attribute__((noreturn)); +#ifdef CONFIG_SMP extern void smp_send_debugger_break (int cpu);
@@ -57,9 +59,7 @@extern int __cpu_disable (void );extern void __cpu_die (unsigned int cpu); -extern void cpu_die (void ) __attribute__((noreturn)); -extern int query_cpu_stopped (unsigned int pcpu); -#endif /* !(CONFIG_SMP) */ +#endif /* CONFIG_SMP */ #define get_hard_smp_processor_id (CPU ) (paca[(CPU)].hw_cpu_id)#define set_hard_smp_processor_id (CPU ,VAL ) \
@@ -70,6 +70,12 @@extern int smp_mpic_probe (void );extern void smp_mpic_setup_cpu (int cpu);extern void smp_mpic_message_pass (int target,int msg); +extern void smp_generic_kick_cpu (int nr); + +extern void smp_generic_give_timebase (void ); +extern void smp_generic_take_timebase (void ); + +extern struct smp_ops_t *smp_ops;#endif /* __ASSEMBLY__ */
arch/ppc64/kernel/iSeries_smp.c new file +151 -0
151 lines not on this page · raw message
arch/ppc64/kernel/pmac_smp.c +19 -13
@@ -21,6 +21,9 @@* as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + +#undef DEBUG +#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h>
@@ -51,6 +54,11 @@#include "mpic.h" +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif extern void pmac_secondary_start_1 (void );extern void pmac_secondary_start_2 (void );
@@ -102,15 +110,16 @@* b .pmac_secondary_start - KERNELBASE */ switch (nr) { -case 1 : - new_vector = (unsigned long )pmac_secondary_start_1; -break ; -case 2 : - new_vector = (unsigned long )pmac_secondary_start_2; -break ; -case 3 : - new_vector = (unsigned long )pmac_secondary_start_3; -break ; +case 1 : + new_vector = (unsigned long )pmac_secondary_start_1; +break ; +case 2 : + new_vector = (unsigned long )pmac_secondary_start_2; +break ; +case 3 : +default : + new_vector = (unsigned long )pmac_secondary_start_3; +break ; } *vector =0x48000002 + (new_vector -KERNELBASE );
@@ -149,13 +158,10 @@*/ if (num_online_cpus () <2 )g5_phy_disable_cpu1 (); -if (ppc_md.progress) ppc_md.progress ("core99_setup_cpu 0 done" ,0x349 ); +if (ppc_md.progress) ppc_md.progress ("smp_core99_setup_cpu 0 done" ,0x349 ); } } -extern void smp_generic_give_timebase (void ); -extern void smp_generic_take_timebase (void ); - struct smp_ops_t core99_smp_ops __pmacdata = { .message_pass = smp_mpic_message_pass, .probe = smp_core99_probe,
arch/ppc64/kernel/pSeries_setup.c +15 -0
@@ -321,6 +321,20 @@ } } +static void pSeries_cpu_die (void ) +{ +local_irq_disable (); +/* Some hardware requires clearing the CPPR, while other hardware does not +* it is safe either way +*/ +pSeriesLP_cppr_info (0 ,0 ); +rtas_stop_self (); +/* Should never get here... */ +BUG (); +for (;;); +} + +/* * Early initialization. Relocation is on but do not reference unbolted pages */
@@ -588,6 +602,7 @@ .power_off = rtas_power_off, .halt = rtas_halt, .panic = rtas_os_term, + .cpu_die = pSeries_cpu_die, .get_boot_time = pSeries_get_boot_time, .get_rtc_time = pSeries_get_rtc_time, .set_rtc_time = pSeries_set_rtc_time,
arch/ppc64/kernel/setup.c +7 -0
@@ -1308,3 +1308,10 @@early_param ("xmon" , early_xmon);#endif +void cpu_die (void ) +{ +if (ppc_md.cpu_die) + ppc_md.cpu_die (); +local_irq_disable (); +for (;;); +}
arch/ppc64/kernel/Makefile +2 -0
@@ -53,6 +53,8 @@ ifdef CONFIG_SMP obj-$(CONFIG_PPC_PMAC) += pmac_smp.o smp-tbsync.o +obj-$(CONFIG_PPC_ISERIES) += iSeries_smp.o +obj-$(CONFIG_PPC_PSERIES) += pSeries_smp.o endif obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o