~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to xen/common/stop_machine.c

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************************************
 
2
 * common/stop_machine.c
 
3
 *
 
4
 * Facilities to put whole machine in a safe 'stop' state
 
5
 *
 
6
 * Copyright 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation
 
7
 * Copyright 2008 Kevin Tian <kevin.tian@intel.com>, Intel Corporation.
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or modify it
 
10
 * under the terms and conditions of the GNU General Public License,
 
11
 * version 2, as published by the Free Software Foundation.
 
12
 *
 
13
 * This program is distributed in the hope it will be useful, but WITHOUT
 
14
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
15
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 
16
 * more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License along with
 
19
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
20
 * Place - Suite 330, Boston, MA 02111-1307 USA.
 
21
 */
 
22
 
 
23
#include <xen/config.h>
 
24
#include <xen/init.h>
 
25
#include <xen/sched.h>
 
26
#include <xen/spinlock.h>
 
27
#include <xen/softirq.h>
 
28
#include <xen/stop_machine.h>
 
29
#include <xen/errno.h>
 
30
#include <xen/smp.h>
 
31
#include <asm/current.h>
 
32
#include <asm/processor.h>
 
33
 
 
34
enum stopmachine_state {
 
35
    STOPMACHINE_START,
 
36
    STOPMACHINE_PREPARE,
 
37
    STOPMACHINE_DISABLE_IRQ,
 
38
    STOPMACHINE_INVOKE,
 
39
    STOPMACHINE_EXIT
 
40
};
 
41
 
 
42
struct stopmachine_data {
 
43
    unsigned int nr_cpus;
 
44
 
 
45
    enum stopmachine_state state;
 
46
    atomic_t done;
 
47
 
 
48
    unsigned int fn_cpu;
 
49
    int fn_result;
 
50
    int (*fn)(void *);
 
51
    void *fn_data;
 
52
};
 
53
 
 
54
static struct stopmachine_data stopmachine_data;
 
55
static DEFINE_SPINLOCK(stopmachine_lock);
 
56
 
 
57
static void stopmachine_set_state(enum stopmachine_state state)
 
58
{
 
59
    atomic_set(&stopmachine_data.done, 0);
 
60
    smp_wmb();
 
61
    stopmachine_data.state = state;
 
62
    while ( atomic_read(&stopmachine_data.done) != stopmachine_data.nr_cpus )
 
63
        cpu_relax();
 
64
}
 
65
 
 
66
int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
 
67
{
 
68
    cpumask_t allbutself;
 
69
    unsigned int i, nr_cpus;
 
70
    int ret;
 
71
 
 
72
    BUG_ON(!local_irq_is_enabled());
 
73
 
 
74
    allbutself = cpu_online_map;
 
75
    cpu_clear(smp_processor_id(), allbutself);
 
76
    nr_cpus = cpus_weight(allbutself);
 
77
 
 
78
    if ( nr_cpus == 0 )
 
79
    {
 
80
        BUG_ON(cpu != smp_processor_id());
 
81
        return (*fn)(data);
 
82
    }
 
83
 
 
84
    /* Note: We shouldn't spin on lock when it's held by others since others
 
85
     * is expecting this cpus to enter softirq context. Or else deadlock
 
86
     * is caused.
 
87
     */
 
88
    if ( !spin_trylock(&stopmachine_lock) )
 
89
        return -EBUSY;
 
90
 
 
91
    stopmachine_data.fn = fn;
 
92
    stopmachine_data.fn_data = data;
 
93
    stopmachine_data.nr_cpus = nr_cpus;
 
94
    stopmachine_data.fn_cpu = cpu;
 
95
    atomic_set(&stopmachine_data.done, 0);
 
96
    stopmachine_data.state = STOPMACHINE_START;
 
97
 
 
98
    smp_wmb();
 
99
 
 
100
    for_each_cpu_mask ( i, allbutself )
 
101
        cpu_raise_softirq(i, STOPMACHINE_SOFTIRQ);
 
102
 
 
103
    stopmachine_set_state(STOPMACHINE_PREPARE);
 
104
 
 
105
    local_irq_disable();
 
106
    stopmachine_set_state(STOPMACHINE_DISABLE_IRQ);
 
107
 
 
108
    if ( cpu == smp_processor_id() )
 
109
        stopmachine_data.fn_result = (*fn)(data);
 
110
    stopmachine_set_state(STOPMACHINE_INVOKE);
 
111
    ret = stopmachine_data.fn_result;
 
112
 
 
113
    stopmachine_set_state(STOPMACHINE_EXIT);
 
114
    local_irq_enable();
 
115
 
 
116
    spin_unlock(&stopmachine_lock);
 
117
 
 
118
    return ret;
 
119
}
 
120
 
 
121
static void stopmachine_softirq(void)
 
122
{
 
123
    enum stopmachine_state state = STOPMACHINE_START;
 
124
 
 
125
    smp_mb();
 
126
 
 
127
    while ( state != STOPMACHINE_EXIT )
 
128
    {
 
129
        while ( stopmachine_data.state == state )
 
130
            cpu_relax();
 
131
 
 
132
        state = stopmachine_data.state;
 
133
        switch ( state )
 
134
        {
 
135
        case STOPMACHINE_DISABLE_IRQ:
 
136
            local_irq_disable();
 
137
            break;
 
138
        case STOPMACHINE_INVOKE:
 
139
            if ( stopmachine_data.fn_cpu == smp_processor_id() )
 
140
                stopmachine_data.fn_result =
 
141
                    stopmachine_data.fn(stopmachine_data.fn_data);
 
142
            break;
 
143
        default:
 
144
            break;
 
145
        }
 
146
 
 
147
        smp_mb();
 
148
        atomic_inc(&stopmachine_data.done);
 
149
    }
 
150
 
 
151
    local_irq_enable();
 
152
}
 
153
 
 
154
static int __init cpu_stopmachine_init(void)
 
155
{
 
156
    open_softirq(STOPMACHINE_SOFTIRQ, stopmachine_softirq);
 
157
    return 0;
 
158
}
 
159
__initcall(cpu_stopmachine_init);