aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--xen/arch/x86/cpu/mtrr/main.c2
-rw-r--r--xen/arch/x86/domain.c3
-rw-r--r--xen/arch/x86/smpboot.c25
-rw-r--r--xen/common/Makefile1
-rw-r--r--xen/common/stop_machine.c168
-rw-r--r--xen/include/asm-x86/smp.h3
-rw-r--r--xen/include/xen/smp.h17
-rw-r--r--xen/include/xen/softirq.h3
-rw-r--r--xen/include/xen/stop_machine.h30
9 files changed, 239 insertions, 13 deletions
diff --git a/xen/arch/x86/cpu/mtrr/main.c b/xen/arch/x86/cpu/mtrr/main.c
index 4881055d1a..2a606f0320 100644
--- a/xen/arch/x86/cpu/mtrr/main.c
+++ b/xen/arch/x86/cpu/mtrr/main.c
@@ -46,8 +46,6 @@
#define DEFINE_MUTEX(_m) DEFINE_SPINLOCK(_m)
#define mutex_lock(_m) spin_lock(_m)
#define mutex_unlock(_m) spin_unlock(_m)
-#define lock_cpu_hotplug() ((void)0)
-#define unlock_cpu_hotplug() ((void)0)
#define dump_stack() ((void)0)
#define get_cpu() smp_processor_id()
#define put_cpu() do {} while(0)
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 9d0537a9f9..25bc2c7148 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -82,7 +82,6 @@ static void default_idle(void)
static void play_dead(void)
{
- __cpu_disable();
/* This must be done before dead CPU ack */
cpu_exit_clear();
hvm_cpu_down();
@@ -101,7 +100,7 @@ void idle_loop(void)
{
for ( ; ; )
{
- if (cpu_is_offline(smp_processor_id()))
+ if ( cpu_is_offline(smp_processor_id()) )
play_dead();
page_scrub_schedule_work();
default_idle();
diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c
index 32668e9455..914ec62689 100644
--- a/xen/arch/x86/smpboot.c
+++ b/xen/arch/x86/smpboot.c
@@ -54,6 +54,7 @@
#include <mach_apic.h>
#include <mach_wakecpu.h>
#include <smpboot_hooks.h>
+#include <xen/stop_machine.h>
#define set_kernel_exec(x, y) (0)
#define setup_trampoline() (bootsym_phys(trampoline_realmode_entry))
@@ -1208,6 +1209,15 @@ int __cpu_disable(void)
if (cpu == 0)
return -EBUSY;
+ /*
+ * Only S3 is using this path, and thus idle vcpus are running on all
+ * APs when we are called. To support full cpu hotplug, other
+ * notification mechanisms should be introduced (e.g., migrate vcpus
+ * off this physical cpu before rendezvous point).
+ */
+ if (!is_idle_vcpu(current))
+ return -EINVAL;
+
local_irq_disable();
clear_local_APIC();
/* Allow any queued timer interrupts to get serviced */
@@ -1244,6 +1254,11 @@ void __cpu_die(unsigned int cpu)
printk(KERN_ERR "CPU %u didn't die...\n", cpu);
}
+static int take_cpu_down(void *unused)
+{
+ return __cpu_disable();
+}
+
/*
* XXX: One important thing missed here is to migrate vcpus
* from dead cpu to other online ones and then put whole
@@ -1269,7 +1284,6 @@ void __cpu_die(unsigned int cpu)
int cpu_down(unsigned int cpu)
{
int err = 0;
- cpumask_t mask;
spin_lock(&cpu_add_remove_lock);
if (num_online_cpus() == 1) {
@@ -1283,11 +1297,10 @@ int cpu_down(unsigned int cpu)
}
printk("Prepare to bring CPU%d down...\n", cpu);
- /* Send notification to remote idle vcpu */
- cpus_clear(mask);
- cpu_set(cpu, mask);
- per_cpu(cpu_state, cpu) = CPU_DYING;
- smp_send_event_check_mask(mask);
+
+ err = __stop_machine_run(take_cpu_down, NULL, cpu);
+ if ( err < 0 )
+ goto out;
__cpu_die(cpu);
diff --git a/xen/common/Makefile b/xen/common/Makefile
index 1bf38310bf..631ac384bd 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -16,6 +16,7 @@ obj-y += sched_sedf.o
obj-y += schedule.o
obj-y += shutdown.o
obj-y += softirq.o
+obj-y += stop_machine.o
obj-y += string.o
obj-y += symbols.o
obj-y += sysctl.o
diff --git a/xen/common/stop_machine.c b/xen/common/stop_machine.c
new file mode 100644
index 0000000000..b109167ef7
--- /dev/null
+++ b/xen/common/stop_machine.c
@@ -0,0 +1,168 @@
+/******************************************************************************
+ * common/stop_machine.c
+ *
+ * Facilities to put whole machine in a safe 'stop' state
+ *
+ * Copyright 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation
+ * Copyright 2008 Kevin Tian <kevin.tian@intel.com>, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <xen/config.h>
+#include <xen/init.h>
+#include <xen/spinlock.h>
+#include <asm/smp.h>
+#include <asm/current.h>
+#include <xen/softirq.h>
+#include <asm/processor.h>
+#include <xen/errno.h>
+
+enum stopmachine_state {
+ STOPMACHINE_START,
+ STOPMACHINE_PREPARE,
+ STOPMACHINE_DISABLE_IRQ,
+ STOPMACHINE_INVOKE,
+ STOPMACHINE_EXIT
+};
+
+struct stopmachine_data {
+ unsigned int nr_cpus;
+
+ enum stopmachine_state state;
+ atomic_t done;
+
+ unsigned int fn_cpu;
+ int fn_result;
+ int (*fn)(void *);
+ void *fn_data;
+};
+
+static struct stopmachine_data stopmachine_data;
+static DEFINE_SPINLOCK(stopmachine_lock);
+
+static void stopmachine_set_state(enum stopmachine_state state)
+{
+ atomic_set(&stopmachine_data.done, 0);
+ smp_wmb();
+ stopmachine_data.state = state;
+ while ( atomic_read(&stopmachine_data.done) != stopmachine_data.nr_cpus )
+ cpu_relax();
+}
+
+int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
+{
+ cpumask_t allbutself;
+ unsigned int i, nr_cpus;
+ int ret;
+
+ BUG_ON(!local_irq_is_enabled());
+
+ allbutself = cpu_online_map;
+ cpu_clear(smp_processor_id(), allbutself);
+ nr_cpus = cpus_weight(allbutself);
+
+ if ( nr_cpus == 0 )
+ {
+ BUG_ON(cpu != smp_processor_id());
+ return (*fn)(data);
+ }
+
+ /* Note: We shouldn't spin on lock when it's held by others since others
+ * is expecting this cpus to enter softirq context. Or else deadlock
+ * is caused.
+ */
+ if ( !spin_trylock(&stopmachine_lock) )
+ return -EBUSY;
+
+ stopmachine_data.fn = fn;
+ stopmachine_data.fn_data = data;
+ stopmachine_data.nr_cpus = nr_cpus;
+ stopmachine_data.fn_cpu = cpu;
+ atomic_set(&stopmachine_data.done, 0);
+ stopmachine_data.state = STOPMACHINE_START;
+
+ smp_wmb();
+
+ for_each_cpu_mask ( i, allbutself )
+ cpu_raise_softirq(i, STOPMACHINE_SOFTIRQ);
+
+ stopmachine_set_state(STOPMACHINE_PREPARE);
+
+ local_irq_disable();
+ stopmachine_set_state(STOPMACHINE_DISABLE_IRQ);
+
+ if ( cpu == smp_processor_id() )
+ stopmachine_data.fn_result = (*fn)(data);
+ stopmachine_set_state(STOPMACHINE_INVOKE);
+ ret = stopmachine_data.fn_result;
+
+ stopmachine_set_state(STOPMACHINE_EXIT);
+ local_irq_enable();
+
+ spin_unlock(&stopmachine_lock);
+
+ return ret;
+}
+
+int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
+{
+ int ret;
+
+ lock_cpu_hotplug();
+ ret = __stop_machine_run(fn, data, cpu);
+ unlock_cpu_hotplug();
+
+ return ret;
+}
+
+static void stopmachine_softirq(void)
+{
+ enum stopmachine_state state = STOPMACHINE_START;
+
+ smp_mb();
+
+ while ( state != STOPMACHINE_EXIT )
+ {
+ while ( stopmachine_data.state == state )
+ cpu_relax();
+
+ state = stopmachine_data.state;
+ switch ( state )
+ {
+ case STOPMACHINE_DISABLE_IRQ:
+ local_irq_disable();
+ break;
+ case STOPMACHINE_INVOKE:
+ if ( stopmachine_data.fn_cpu == smp_processor_id() )
+ stopmachine_data.fn_result =
+ stopmachine_data.fn(stopmachine_data.fn_data);
+ break;
+ default:
+ break;
+ }
+
+ smp_mb();
+ atomic_inc(&stopmachine_data.done);
+ }
+
+ local_irq_enable();
+}
+
+static int __init cpu_stopmachine_init(void)
+{
+ open_softirq(STOPMACHINE_SOFTIRQ, stopmachine_softirq);
+ return 0;
+}
+__initcall(cpu_stopmachine_init);
diff --git a/xen/include/asm-x86/smp.h b/xen/include/asm-x86/smp.h
index 55b8cf7705..cf5159603d 100644
--- a/xen/include/asm-x86/smp.h
+++ b/xen/include/asm-x86/smp.h
@@ -51,12 +51,11 @@ extern u8 x86_cpu_to_apicid[];
/* State of each CPU. */
#define CPU_ONLINE 0x0002 /* CPU is up */
-#define CPU_DYING 0x0003 /* CPU is requested to die */
#define CPU_DEAD 0x0004 /* CPU is dead */
DECLARE_PER_CPU(int, cpu_state);
#ifdef CONFIG_HOTPLUG_CPU
-#define cpu_is_offline(cpu) unlikely(per_cpu(cpu_state,cpu) == CPU_DYING)
+#define cpu_is_offline(cpu) unlikely(!cpu_online(cpu))
extern int cpu_down(unsigned int cpu);
extern int cpu_up(unsigned int cpu);
extern void cpu_exit_clear(void);
diff --git a/xen/include/xen/smp.h b/xen/include/xen/smp.h
index c02ecd4dc3..3e7de77ec8 100644
--- a/xen/include/xen/smp.h
+++ b/xen/include/xen/smp.h
@@ -112,4 +112,21 @@ static inline int on_each_cpu(
#define smp_processor_id() raw_smp_processor_id()
+#ifdef CONFIG_HOTPLUG_CPU
+extern spinlock_t cpu_add_remove_lock;
+/*
+ * FIXME: need a better lock mechanism when real cpu hotplug is later
+ * supported, since spinlock may cause dead lock:
+ * cpu0: in stop_machine with lock held. Wait for cpu1 to respond
+ * to stop request
+ * cpu1: spin loop on lock upon cpu hotplug request from guest,
+ * without chance to handle softirq
+ * ...
+ */
+#define lock_cpu_hotplug() spin_lock(&cpu_add_remove_lock);
+#define unlock_cpu_hotplug() spin_unlock(&cpu_add_remove_lock);
+#else
+#define lock_cpu_hotplug() do { } while ( 0 )
+#define unlock_cpu_hotplug() do { } while ( 0 )
+#endif
#endif
diff --git a/xen/include/xen/softirq.h b/xen/include/xen/softirq.h
index 9a66e0fb03..7734b3374d 100644
--- a/xen/include/xen/softirq.h
+++ b/xen/include/xen/softirq.h
@@ -10,8 +10,9 @@
#define PAGE_SCRUB_SOFTIRQ 5
#define TRACE_SOFTIRQ 6
#define RCU_SOFTIRQ 7
+#define STOPMACHINE_SOFTIRQ 8
-#define NR_COMMON_SOFTIRQS 8
+#define NR_COMMON_SOFTIRQS 9
#include <asm/softirq.h>
diff --git a/xen/include/xen/stop_machine.h b/xen/include/xen/stop_machine.h
new file mode 100644
index 0000000000..f379f6705a
--- /dev/null
+++ b/xen/include/xen/stop_machine.h
@@ -0,0 +1,30 @@
+#ifndef __XEN_STOP_MACHINE_H__
+#define __XEN_STOP_MACHINE_H__
+
+/**
+ * stop_machine_run: freeze the machine on all CPUs and run this function
+ * @fn: the function to run
+ * @data: the data ptr for the @fn()
+ * @cpu: the cpu to run @fn() on (or any, if @cpu == NR_CPUS).
+ *
+ * Description: This causes every other cpu to enter a safe point, with
+ * each of which disables interrupts, and finally interrupts are disabled
+ * on the current CPU. The result is that none is holding a spinlock
+ * or inside any other preempt-disabled region when @fn() runs.
+ *
+ * This can be thought of as a very heavy write lock, equivalent to
+ * grabbing every spinlock in the kernel. */
+int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu);
+
+/**
+ * __stop_machine_run: freeze the machine on all CPUs and run this function
+ * @fn: the function to run
+ * @data: the data ptr for the @fn
+ * @cpu: the cpu to run @fn on (or any, if @cpu == NR_CPUS.
+ *
+ * Description: This is a special version of the above, without explicit
+ * lock acquisition. Used by hotplug cpu.
+ */
+int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu);
+
+#endif /* __XEN_STOP_MACHINE_H__ */