aboutsummaryrefslogtreecommitdiffstats
path: root/xen
diff options
context:
space:
mode:
authorTim Deegan <tim@xen.org>2012-03-13 15:10:56 +0000
committerTim Deegan <tim@xen.org>2012-03-13 15:10:56 +0000
commit5e40a1b4351e7f6460ed065585ad56a19c488a51 (patch)
tree1a6a6256dd1d30433282cdc814d1fa954158ac15 /xen
parentb8bac01f1a0e3583b9815c331763cf068175aa11 (diff)
downloadxen-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.c10
-rw-r--r--xen/arch/arm/gic.c19
-rw-r--r--xen/arch/arm/gic.h2
-rw-r--r--xen/arch/arm/smpboot.c48
-rw-r--r--xen/include/asm-arm/smp.h2
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: