debuggers.hg

annotate xen/common/stop_machine.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents 2d4fe1a048cd
children
rev   line source
keir@17061 1 /******************************************************************************
keir@17061 2 * common/stop_machine.c
keir@17061 3 *
keir@17061 4 * Facilities to put whole machine in a safe 'stop' state
keir@17061 5 *
keir@17061 6 * Copyright 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation
keir@17061 7 * Copyright 2008 Kevin Tian <kevin.tian@intel.com>, Intel Corporation.
keir@17061 8 *
keir@17061 9 * This program is free software; you can redistribute it and/or modify it
keir@17061 10 * under the terms and conditions of the GNU General Public License,
keir@17061 11 * version 2, as published by the Free Software Foundation.
keir@17061 12 *
keir@17061 13 * This program is distributed in the hope it will be useful, but WITHOUT
keir@17061 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
keir@17061 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
keir@17061 16 * more details.
keir@17061 17 *
keir@17061 18 * You should have received a copy of the GNU General Public License along with
keir@17061 19 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
keir@17061 20 * Place - Suite 330, Boston, MA 02111-1307 USA.
keir@17061 21 */
keir@17061 22
keir@17061 23 #include <xen/config.h>
keir@17061 24 #include <xen/init.h>
keir@17080 25 #include <xen/sched.h>
keir@17061 26 #include <xen/spinlock.h>
keir@21248 27 #include <xen/tasklet.h>
keir@18096 28 #include <xen/stop_machine.h>
keir@17080 29 #include <xen/errno.h>
keir@17080 30 #include <xen/smp.h>
keir@21430 31 #include <xen/cpu.h>
keir@17061 32 #include <asm/current.h>
keir@17061 33 #include <asm/processor.h>
keir@17061 34
keir@17061 35 enum stopmachine_state {
keir@17061 36 STOPMACHINE_START,
keir@17061 37 STOPMACHINE_PREPARE,
keir@17061 38 STOPMACHINE_DISABLE_IRQ,
keir@17061 39 STOPMACHINE_INVOKE,
keir@17061 40 STOPMACHINE_EXIT
keir@17061 41 };
keir@17061 42
keir@17061 43 struct stopmachine_data {
keir@17061 44 unsigned int nr_cpus;
keir@17061 45
keir@17061 46 enum stopmachine_state state;
keir@17061 47 atomic_t done;
keir@17061 48
keir@18977 49 unsigned int fn_cpu;
keir@17061 50 int fn_result;
keir@17061 51 int (*fn)(void *);
keir@17061 52 void *fn_data;
keir@17061 53 };
keir@17061 54
keir@21248 55 static DEFINE_PER_CPU(struct tasklet, stopmachine_tasklet);
keir@17061 56 static struct stopmachine_data stopmachine_data;
keir@17061 57 static DEFINE_SPINLOCK(stopmachine_lock);
keir@17061 58
keir@17061 59 static void stopmachine_set_state(enum stopmachine_state state)
keir@17061 60 {
keir@17061 61 atomic_set(&stopmachine_data.done, 0);
keir@17061 62 smp_wmb();
keir@17061 63 stopmachine_data.state = state;
keir@22801 64 }
keir@22801 65
keir@22801 66 static void stopmachine_wait_state(void)
keir@22801 67 {
keir@17061 68 while ( atomic_read(&stopmachine_data.done) != stopmachine_data.nr_cpus )
keir@17061 69 cpu_relax();
keir@17061 70 }
keir@17061 71
keir@18977 72 int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
keir@17061 73 {
keir@17061 74 cpumask_t allbutself;
keir@17061 75 unsigned int i, nr_cpus;
keir@18977 76 int ret;
keir@17061 77
keir@17061 78 BUG_ON(!local_irq_is_enabled());
keir@17061 79
keir@21430 80 /* cpu_online_map must not change. */
keir@21430 81 if ( !get_cpu_maps() )
keir@21430 82 return -EBUSY;
keir@21430 83
keir@17061 84 allbutself = cpu_online_map;
keir@18977 85 cpu_clear(smp_processor_id(), allbutself);
keir@17061 86 nr_cpus = cpus_weight(allbutself);
keir@17061 87
keir@21248 88 /* Must not spin here as the holder will expect us to be descheduled. */
keir@17061 89 if ( !spin_trylock(&stopmachine_lock) )
keir@21430 90 {
keir@21430 91 put_cpu_maps();
keir@17061 92 return -EBUSY;
keir@21430 93 }
keir@17061 94
keir@17061 95 stopmachine_data.fn = fn;
keir@17061 96 stopmachine_data.fn_data = data;
keir@17061 97 stopmachine_data.nr_cpus = nr_cpus;
keir@18977 98 stopmachine_data.fn_cpu = cpu;
keir@17061 99 atomic_set(&stopmachine_data.done, 0);
keir@17061 100 stopmachine_data.state = STOPMACHINE_START;
keir@17061 101
keir@17061 102 smp_wmb();
keir@17061 103
keir@17061 104 for_each_cpu_mask ( i, allbutself )
keir@21248 105 tasklet_schedule_on_cpu(&per_cpu(stopmachine_tasklet, i), i);
keir@17061 106
keir@17061 107 stopmachine_set_state(STOPMACHINE_PREPARE);
keir@22801 108 stopmachine_wait_state();
keir@17061 109
keir@17061 110 local_irq_disable();
keir@17061 111 stopmachine_set_state(STOPMACHINE_DISABLE_IRQ);
keir@22801 112 stopmachine_wait_state();
keir@17061 113
keir@22801 114 stopmachine_set_state(STOPMACHINE_INVOKE);
keir@22801 115 if ( (cpu == smp_processor_id()) || (cpu == NR_CPUS) )
keir@18977 116 stopmachine_data.fn_result = (*fn)(data);
keir@22801 117 stopmachine_wait_state();
keir@17061 118 ret = stopmachine_data.fn_result;
keir@17061 119
keir@17061 120 stopmachine_set_state(STOPMACHINE_EXIT);
keir@22801 121 stopmachine_wait_state();
keir@17061 122 local_irq_enable();
keir@17061 123
keir@17061 124 spin_unlock(&stopmachine_lock);
keir@17061 125
keir@21430 126 put_cpu_maps();
keir@21430 127
keir@17061 128 return ret;
keir@17061 129 }
keir@17061 130
keir@21430 131 static void stopmachine_action(unsigned long cpu)
keir@17061 132 {
keir@17061 133 enum stopmachine_state state = STOPMACHINE_START;
keir@18977 134
keir@21430 135 BUG_ON(cpu != smp_processor_id());
keir@21430 136
keir@17061 137 smp_mb();
keir@17061 138
keir@17061 139 while ( state != STOPMACHINE_EXIT )
keir@17061 140 {
keir@17061 141 while ( stopmachine_data.state == state )
keir@17061 142 cpu_relax();
keir@17061 143
keir@17061 144 state = stopmachine_data.state;
keir@17061 145 switch ( state )
keir@17061 146 {
keir@17061 147 case STOPMACHINE_DISABLE_IRQ:
keir@17061 148 local_irq_disable();
keir@17061 149 break;
keir@17061 150 case STOPMACHINE_INVOKE:
keir@22801 151 if ( (stopmachine_data.fn_cpu == smp_processor_id()) ||
keir@22801 152 (stopmachine_data.fn_cpu == NR_CPUS) )
keir@18977 153 stopmachine_data.fn_result =
keir@17061 154 stopmachine_data.fn(stopmachine_data.fn_data);
keir@18977 155 break;
keir@17061 156 default:
keir@17061 157 break;
keir@17061 158 }
keir@17061 159
keir@17061 160 smp_mb();
keir@17061 161 atomic_inc(&stopmachine_data.done);
keir@17061 162 }
keir@17061 163
keir@17061 164 local_irq_enable();
keir@17061 165 }
keir@17061 166
keir@21436 167 static int cpu_callback(
keir@21436 168 struct notifier_block *nfb, unsigned long action, void *hcpu)
keir@21436 169 {
keir@21436 170 unsigned int cpu = (unsigned long)hcpu;
keir@21436 171
keir@21436 172 if ( action == CPU_UP_PREPARE )
keir@21436 173 tasklet_init(&per_cpu(stopmachine_tasklet, cpu),
keir@21436 174 stopmachine_action, cpu);
keir@21436 175
keir@21436 176 return NOTIFY_DONE;
keir@21436 177 }
keir@21436 178
keir@21436 179 static struct notifier_block cpu_nfb = {
keir@21436 180 .notifier_call = cpu_callback
keir@21436 181 };
keir@21436 182
keir@17061 183 static int __init cpu_stopmachine_init(void)
keir@17061 184 {
keir@21248 185 unsigned int cpu;
keir@21436 186 for_each_online_cpu ( cpu )
keir@21436 187 {
keir@21436 188 void *hcpu = (void *)(long)cpu;
keir@21436 189 cpu_callback(&cpu_nfb, CPU_UP_PREPARE, hcpu);
keir@21436 190 }
keir@21436 191 register_cpu_notifier(&cpu_nfb);
keir@17061 192 return 0;
keir@17061 193 }
keir@17061 194 __initcall(cpu_stopmachine_init);