diff options
author | Tim Deegan <tim@xen.org> | 2012-03-13 15:10:56 +0000 |
---|---|---|
committer | Tim Deegan <tim@xen.org> | 2012-03-13 15:10:56 +0000 |
commit | 5e40a1b4351e7f6460ed065585ad56a19c488a51 (patch) | |
tree | 1a6a6256dd1d30433282cdc814d1fa954158ac15 /xen | |
parent | b8bac01f1a0e3583b9815c331763cf068175aa11 (diff) | |
download | xen-5e40a1b4351e7f6460ed065585ad56a19c488a51.tar.gz xen-5e40a1b4351e7f6460ed065585ad56a19c488a51.tar.bz2 xen-5e40a1b4351e7f6460ed065585ad56a19c488a51.zip |
arm: SMP CPU shutdown
For completeness, also implelent the CPU shutdown path.
Signed-off-by: Tim Deegan <tim@xen.org>
Committed-by: Ian Campbell <ian.campbell@citrix.com>
Diffstat (limited to 'xen')
-rw-r--r-- | xen/arch/arm/domain.c | 10 | ||||
-rw-r--r-- | xen/arch/arm/gic.c | 19 | ||||
-rw-r--r-- | xen/arch/arm/gic.h | 2 | ||||
-rw-r--r-- | xen/arch/arm/smpboot.c | 48 | ||||
-rw-r--r-- | xen/include/asm-arm/smp.h | 2 |
5 files changed, 71 insertions, 10 deletions
diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c index a0b9d38740..e1d500df5c 100644 --- a/xen/arch/arm/domain.c +++ b/xen/arch/arm/domain.c @@ -31,12 +31,10 @@ void idle_loop(void) { for ( ; ; ) { - /* TODO - if ( cpu_is_offline(smp_processor_id()) ) - play_dead(); - (*pm_idle)(); - BUG(); - */ + if ( cpu_is_offline(smp_processor_id()) ) + stop_cpu(); + + /* TODO: (*pm_idle)(); */ do_tasklet(); do_softirq(); } diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 6a4ebb1a47..15c315fd2b 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -239,6 +239,11 @@ static void __cpuinit gic_cpu_init(void) GICC[GICC_CTLR] = GICC_CTL_ENABLE|GICC_CTL_EOI; /* Turn on delivery */ } +static void gic_cpu_disable(void) +{ + GICC[GICC_CTLR] = 0; +} + static void __cpuinit gic_hyp_init(void) { uint32_t vtr; @@ -250,6 +255,11 @@ static void __cpuinit gic_hyp_init(void) GICH[GICH_MISR] = GICH_MISR_EOI; } +static void __cpuinit gic_hyp_disable(void) +{ + GICH[GICH_HCR] = 0; +} + /* Set up the GIC */ int __init gic_init(void) { @@ -286,6 +296,15 @@ void __cpuinit gic_init_secondary_cpu(void) spin_unlock(&gic.lock); } +/* Shut down the per-CPU GIC interface */ +void gic_disable_cpu(void) +{ + spin_lock(&gic.lock); + gic_cpu_disable(); + gic_hyp_disable(); + spin_unlock(&gic.lock); +} + void gic_route_irqs(void) { /* XXX should get these from DT */ diff --git a/xen/arch/arm/gic.h b/xen/arch/arm/gic.h index c79ba33c1d..6b2be4f1d8 100644 --- a/xen/arch/arm/gic.h +++ b/xen/arch/arm/gic.h @@ -142,6 +142,8 @@ extern void gic_interrupt(struct cpu_user_regs *regs, int is_fiq); extern int gic_init(void); /* Bring up a secondary CPU's per-CPU GIC interface */ extern void gic_init_secondary_cpu(void); +/* Take down a CPU's per-CPU GIC interface */ +extern void gic_disable_cpu(void); /* setup the gic virtual interface for a guest */ extern void gicv_setup(struct domain *d); #endif diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c index 8119ef2439..b9b63c1fcb 100644 --- a/xen/arch/arm/smpboot.c +++ b/xen/arch/arm/smpboot.c @@ -18,6 +18,7 @@ #include <xen/cpu.h> #include <xen/cpumask.h> +#include <xen/delay.h> #include <xen/errno.h> #include <xen/init.h> #include <xen/mm.h> @@ -57,6 +58,7 @@ smp_prepare_cpus (unsigned int max_cpus) /* Shared state for coordinating CPU bringup */ unsigned long smp_up_cpu = 0; +static bool_t cpu_is_dead = 0; /* Boot the current CPU */ void __cpuinit start_secondary(unsigned long boot_phys_offset, @@ -97,8 +99,35 @@ void __cpuinit start_secondary(unsigned long boot_phys_offset, /* Shut down the current CPU */ void __cpu_disable(void) { - /* TODO: take down timers, GIC, &c. */ - BUG(); + unsigned int cpu = get_processor_id(); + + local_irq_disable(); + gic_disable_cpu(); + /* Allow any queued timer interrupts to get serviced */ + local_irq_enable(); + mdelay(1); + local_irq_disable(); + + /* It's now safe to remove this processor from the online map */ + cpumask_clear_cpu(cpu, &cpu_online_map); + + if ( cpu_disable_scheduler(cpu) ) + BUG(); + mb(); + + /* Return to caller; eventually the IPI mechanism will unwind and the + * scheduler will drop to the idle loop, which will call stop_cpu(). */ +} + +void stop_cpu(void) +{ + local_irq_disable(); + cpu_is_dead = 1; + /* Make sure the write happens before we sleep forever */ + dsb(); + isb(); + while ( 1 ) + asm volatile("wfi"); } /* Bring up a remote CPU */ @@ -124,8 +153,19 @@ int __cpu_up(unsigned int cpu) /* Wait for a remote CPU to die */ void __cpu_die(unsigned int cpu) { - /* TODO: interlock with __cpu_disable */ - BUG(); + unsigned int i = 0; + + while ( !cpu_is_dead ) + { + mdelay(100); + cpu_relax(); + process_pending_softirqs(); + if ( (++i % 10) == 0 ) + printk(KERN_ERR "CPU %u still not dead...\n", cpu); + mb(); + } + cpu_is_dead = 0; + mb(); } diff --git a/xen/include/asm-arm/smp.h b/xen/include/asm-arm/smp.h index 9cdd87ffb2..8099f6da4f 100644 --- a/xen/include/asm-arm/smp.h +++ b/xen/include/asm-arm/smp.h @@ -14,6 +14,8 @@ DECLARE_PER_CPU(cpumask_var_t, cpu_core_mask); #define raw_smp_processor_id() (get_processor_id()) +extern void stop_cpu(void); + #endif /* * Local variables: |