]>
Commit | Line | Data |
---|---|---|
ffdb5976 | 1 | /* Copyright 2008, 2005 Rusty Russell [email protected] IBM Corporation. |
e5582ca2 RR |
2 | * GPL v2 and any later version. |
3 | */ | |
1da177e4 LT |
4 | #include <linux/cpu.h> |
5 | #include <linux/err.h> | |
ee527cd3 PB |
6 | #include <linux/kthread.h> |
7 | #include <linux/module.h> | |
8 | #include <linux/sched.h> | |
9 | #include <linux/stop_machine.h> | |
1da177e4 | 10 | #include <linux/syscalls.h> |
a12bb444 BH |
11 | #include <linux/interrupt.h> |
12 | ||
1da177e4 | 13 | #include <asm/atomic.h> |
1da177e4 LT |
14 | #include <asm/uaccess.h> |
15 | ||
ffdb5976 | 16 | /* This controls the threads on each CPU. */ |
1da177e4 | 17 | enum stopmachine_state { |
ffdb5976 RR |
18 | /* Dummy starting state for thread. */ |
19 | STOPMACHINE_NONE, | |
20 | /* Awaiting everyone to be scheduled. */ | |
1da177e4 | 21 | STOPMACHINE_PREPARE, |
ffdb5976 | 22 | /* Disable interrupts. */ |
1da177e4 | 23 | STOPMACHINE_DISABLE_IRQ, |
ffdb5976 | 24 | /* Run the function */ |
5c2aed62 | 25 | STOPMACHINE_RUN, |
ffdb5976 | 26 | /* Exit */ |
1da177e4 LT |
27 | STOPMACHINE_EXIT, |
28 | }; | |
ffdb5976 | 29 | static enum stopmachine_state state; |
1da177e4 | 30 | |
5c2aed62 JB |
31 | struct stop_machine_data { |
32 | int (*fn)(void *); | |
33 | void *data; | |
ffdb5976 RR |
34 | int fnret; |
35 | }; | |
5c2aed62 | 36 | |
ffdb5976 RR |
37 | /* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */ |
38 | static unsigned int num_threads; | |
39 | static atomic_t thread_ack; | |
ffdb5976 | 40 | static DEFINE_MUTEX(lock); |
9ea09af3 HC |
41 | /* setup_lock protects refcount, stop_machine_wq and stop_machine_work. */ |
42 | static DEFINE_MUTEX(setup_lock); | |
43 | /* Users of stop_machine. */ | |
44 | static int refcount; | |
c9583e55 HC |
45 | static struct workqueue_struct *stop_machine_wq; |
46 | static struct stop_machine_data active, idle; | |
612a726f | 47 | static const struct cpumask *active_cpus; |
c9583e55 HC |
48 | static void *stop_machine_work; |
49 | ||
ffdb5976 | 50 | static void set_state(enum stopmachine_state newstate) |
1da177e4 | 51 | { |
ffdb5976 RR |
52 | /* Reset ack counter. */ |
53 | atomic_set(&thread_ack, num_threads); | |
54 | smp_wmb(); | |
55 | state = newstate; | |
1da177e4 LT |
56 | } |
57 | ||
ffdb5976 RR |
58 | /* Last one to ack a state moves to the next state. */ |
59 | static void ack_state(void) | |
1da177e4 | 60 | { |
c9583e55 HC |
61 | if (atomic_dec_and_test(&thread_ack)) |
62 | set_state(state + 1); | |
1da177e4 LT |
63 | } |
64 | ||
c9583e55 HC |
65 | /* This is the actual function which stops the CPU. It runs |
66 | * in the context of a dedicated stopmachine workqueue. */ | |
67 | static void stop_cpu(struct work_struct *unused) | |
1da177e4 | 68 | { |
ffdb5976 | 69 | enum stopmachine_state curstate = STOPMACHINE_NONE; |
c9583e55 HC |
70 | struct stop_machine_data *smdata = &idle; |
71 | int cpu = smp_processor_id(); | |
8163bcac | 72 | int err; |
c9583e55 HC |
73 | |
74 | if (!active_cpus) { | |
41c7bb95 | 75 | if (cpu == cpumask_first(cpu_online_mask)) |
c9583e55 HC |
76 | smdata = &active; |
77 | } else { | |
41c7bb95 | 78 | if (cpumask_test_cpu(cpu, active_cpus)) |
c9583e55 HC |
79 | smdata = &active; |
80 | } | |
ffdb5976 RR |
81 | /* Simple state machine */ |
82 | do { | |
83 | /* Chill out and ensure we re-read stopmachine_state. */ | |
3401a61e | 84 | cpu_relax(); |
ffdb5976 RR |
85 | if (state != curstate) { |
86 | curstate = state; | |
87 | switch (curstate) { | |
88 | case STOPMACHINE_DISABLE_IRQ: | |
89 | local_irq_disable(); | |
90 | hard_irq_disable(); | |
91 | break; | |
92 | case STOPMACHINE_RUN: | |
8163bcac HC |
93 | /* On multiple CPUs only a single error code |
94 | * is needed to tell that something failed. */ | |
95 | err = smdata->fn(smdata->data); | |
96 | if (err) | |
97 | smdata->fnret = err; | |
ffdb5976 RR |
98 | break; |
99 | default: | |
100 | break; | |
101 | } | |
102 | ack_state(); | |
103 | } | |
104 | } while (curstate != STOPMACHINE_EXIT); | |
1da177e4 | 105 | |
1da177e4 LT |
106 | local_irq_enable(); |
107 | } | |
108 | ||
ffdb5976 RR |
109 | /* Callback for CPUs which aren't supposed to do anything. */ |
110 | static int chill(void *unused) | |
5c2aed62 | 111 | { |
ffdb5976 | 112 | return 0; |
5c2aed62 | 113 | } |
1da177e4 | 114 | |
9ea09af3 HC |
115 | int stop_machine_create(void) |
116 | { | |
117 | mutex_lock(&setup_lock); | |
118 | if (refcount) | |
119 | goto done; | |
120 | stop_machine_wq = create_rt_workqueue("kstop"); | |
121 | if (!stop_machine_wq) | |
122 | goto err_out; | |
123 | stop_machine_work = alloc_percpu(struct work_struct); | |
124 | if (!stop_machine_work) | |
125 | goto err_out; | |
126 | done: | |
127 | refcount++; | |
128 | mutex_unlock(&setup_lock); | |
129 | return 0; | |
130 | ||
131 | err_out: | |
132 | if (stop_machine_wq) | |
133 | destroy_workqueue(stop_machine_wq); | |
134 | mutex_unlock(&setup_lock); | |
135 | return -ENOMEM; | |
136 | } | |
137 | EXPORT_SYMBOL_GPL(stop_machine_create); | |
138 | ||
139 | void stop_machine_destroy(void) | |
140 | { | |
141 | mutex_lock(&setup_lock); | |
142 | refcount--; | |
143 | if (refcount) | |
144 | goto done; | |
145 | destroy_workqueue(stop_machine_wq); | |
146 | free_percpu(stop_machine_work); | |
147 | done: | |
148 | mutex_unlock(&setup_lock); | |
149 | } | |
150 | EXPORT_SYMBOL_GPL(stop_machine_destroy); | |
151 | ||
41c7bb95 | 152 | int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) |
1da177e4 | 153 | { |
c9583e55 | 154 | struct work_struct *sm_work; |
e14c8bf8 | 155 | int i, ret; |
ffdb5976 | 156 | |
c9583e55 HC |
157 | /* Set up initial state. */ |
158 | mutex_lock(&lock); | |
159 | num_threads = num_online_cpus(); | |
160 | active_cpus = cpus; | |
ffdb5976 RR |
161 | active.fn = fn; |
162 | active.data = data; | |
163 | active.fnret = 0; | |
164 | idle.fn = chill; | |
165 | idle.data = NULL; | |
166 | ||
ffdb5976 | 167 | set_state(STOPMACHINE_PREPARE); |
1da177e4 | 168 | |
c9583e55 | 169 | /* Schedule the stop_cpu work on all cpus: hold this CPU so one |
ffdb5976 | 170 | * doesn't hit this CPU until we're ready. */ |
eeec4fad | 171 | get_cpu(); |
c9583e55 | 172 | for_each_online_cpu(i) { |
b36128c8 | 173 | sm_work = per_cpu_ptr(stop_machine_work, i); |
c9583e55 HC |
174 | INIT_WORK(sm_work, stop_cpu); |
175 | queue_work_on(i, stop_machine_wq, sm_work); | |
176 | } | |
ffdb5976 RR |
177 | /* This will release the thread on our CPU. */ |
178 | put_cpu(); | |
c9583e55 | 179 | flush_workqueue(stop_machine_wq); |
e14c8bf8 | 180 | ret = active.fnret; |
ffdb5976 | 181 | mutex_unlock(&lock); |
e14c8bf8 | 182 | return ret; |
1da177e4 LT |
183 | } |
184 | ||
41c7bb95 | 185 | int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) |
1da177e4 | 186 | { |
1da177e4 LT |
187 | int ret; |
188 | ||
9ea09af3 HC |
189 | ret = stop_machine_create(); |
190 | if (ret) | |
191 | return ret; | |
1da177e4 | 192 | /* No CPUs can come up or down during this. */ |
86ef5c9a | 193 | get_online_cpus(); |
eeec4fad | 194 | ret = __stop_machine(fn, data, cpus); |
86ef5c9a | 195 | put_online_cpus(); |
9ea09af3 | 196 | stop_machine_destroy(); |
1da177e4 LT |
197 | return ret; |
198 | } | |
eeec4fad | 199 | EXPORT_SYMBOL_GPL(stop_machine); |