summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/smpboot.h43
-rw-r--r--kernel/cpu.c10
-rw-r--r--kernel/smpboot.c229
-rw-r--r--kernel/smpboot.h4
4 files changed, 285 insertions, 1 deletions
diff --git a/include/linux/smpboot.h b/include/linux/smpboot.h
new file mode 100644
index 000000000000..e0106d8581d3
--- /dev/null
+++ b/include/linux/smpboot.h
@@ -0,0 +1,43 @@
1#ifndef _LINUX_SMPBOOT_H
2#define _LINUX_SMPBOOT_H
3
4#include <linux/types.h>
5
6struct task_struct;
7/* Cookie handed to the thread_fn*/
8struct smpboot_thread_data;
9
10/**
11 * struct smp_hotplug_thread - CPU hotplug related thread descriptor
12 * @store: Pointer to per cpu storage for the task pointers
13 * @list: List head for core management
14 * @thread_should_run: Check whether the thread should run or not. Called with
15 * preemption disabled.
16 * @thread_fn: The associated thread function
17 * @setup: Optional setup function, called when the thread gets
18 * operational the first time
19 * @cleanup: Optional cleanup function, called when the thread
20 * should stop (module exit)
21 * @park: Optional park function, called when the thread is
22 * parked (cpu offline)
23 * @unpark: Optional unpark function, called when the thread is
24 * unparked (cpu online)
25 * @thread_comm: The base name of the thread
26 */
27struct smp_hotplug_thread {
28 struct task_struct __percpu **store;
29 struct list_head list;
30 int (*thread_should_run)(unsigned int cpu);
31 void (*thread_fn)(unsigned int cpu);
32 void (*setup)(unsigned int cpu);
33 void (*cleanup)(unsigned int cpu, bool online);
34 void (*park)(unsigned int cpu);
35 void (*unpark)(unsigned int cpu);
36 const char *thread_comm;
37};
38
39int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread);
40void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread);
41int smpboot_thread_schedule(void);
42
43#endif
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 14d32588cccd..e615dfbcf794 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -280,12 +280,13 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
280 __func__, cpu); 280 __func__, cpu);
281 goto out_release; 281 goto out_release;
282 } 282 }
283 smpboot_park_threads(cpu);
283 284
284 err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu)); 285 err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
285 if (err) { 286 if (err) {
286 /* CPU didn't die: tell everyone. Can't complain. */ 287 /* CPU didn't die: tell everyone. Can't complain. */
288 smpboot_unpark_threads(cpu);
287 cpu_notify_nofail(CPU_DOWN_FAILED | mod, hcpu); 289 cpu_notify_nofail(CPU_DOWN_FAILED | mod, hcpu);
288
289 goto out_release; 290 goto out_release;
290 } 291 }
291 BUG_ON(cpu_online(cpu)); 292 BUG_ON(cpu_online(cpu));
@@ -354,6 +355,10 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
354 goto out; 355 goto out;
355 } 356 }
356 357
358 ret = smpboot_create_threads(cpu);
359 if (ret)
360 goto out;
361
357 ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls); 362 ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls);
358 if (ret) { 363 if (ret) {
359 nr_calls--; 364 nr_calls--;
@@ -368,6 +373,9 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
368 goto out_notify; 373 goto out_notify;
369 BUG_ON(!cpu_online(cpu)); 374 BUG_ON(!cpu_online(cpu));
370 375
376 /* Wake the per cpu threads */
377 smpboot_unpark_threads(cpu);
378
371 /* Now call notifier in preparation. */ 379 /* Now call notifier in preparation. */
372 cpu_notify(CPU_ONLINE | mod, hcpu); 380 cpu_notify(CPU_ONLINE | mod, hcpu);
373 381
diff --git a/kernel/smpboot.c b/kernel/smpboot.c
index 98f60c5caa1b..9d5f7b04025d 100644
--- a/kernel/smpboot.c
+++ b/kernel/smpboot.c
@@ -1,11 +1,17 @@
1/* 1/*
2 * Common SMP CPU bringup/teardown functions 2 * Common SMP CPU bringup/teardown functions
3 */ 3 */
4#include <linux/cpu.h>
4#include <linux/err.h> 5#include <linux/err.h>
5#include <linux/smp.h> 6#include <linux/smp.h>
6#include <linux/init.h> 7#include <linux/init.h>
8#include <linux/list.h>
9#include <linux/slab.h>
7#include <linux/sched.h> 10#include <linux/sched.h>
11#include <linux/export.h>
8#include <linux/percpu.h> 12#include <linux/percpu.h>
13#include <linux/kthread.h>
14#include <linux/smpboot.h>
9 15
10#include "smpboot.h" 16#include "smpboot.h"
11 17
@@ -65,3 +71,226 @@ void __init idle_threads_init(void)
65 } 71 }
66} 72}
67#endif 73#endif
74
75static LIST_HEAD(hotplug_threads);
76static DEFINE_MUTEX(smpboot_threads_lock);
77
78struct smpboot_thread_data {
79 unsigned int cpu;
80 unsigned int status;
81 struct smp_hotplug_thread *ht;
82};
83
84enum {
85 HP_THREAD_NONE = 0,
86 HP_THREAD_ACTIVE,
87 HP_THREAD_PARKED,
88};
89
90/**
91 * smpboot_thread_fn - percpu hotplug thread loop function
92 * @data: thread data pointer
93 *
94 * Checks for thread stop and park conditions. Calls the necessary
95 * setup, cleanup, park and unpark functions for the registered
96 * thread.
97 *
98 * Returns 1 when the thread should exit, 0 otherwise.
99 */
100static int smpboot_thread_fn(void *data)
101{
102 struct smpboot_thread_data *td = data;
103 struct smp_hotplug_thread *ht = td->ht;
104
105 while (1) {
106 set_current_state(TASK_INTERRUPTIBLE);
107 preempt_disable();
108 if (kthread_should_stop()) {
109 set_current_state(TASK_RUNNING);
110 preempt_enable();
111 if (ht->cleanup)
112 ht->cleanup(td->cpu, cpu_online(td->cpu));
113 kfree(td);
114 return 0;
115 }
116
117 if (kthread_should_park()) {
118 __set_current_state(TASK_RUNNING);
119 preempt_enable();
120 if (ht->park && td->status == HP_THREAD_ACTIVE) {
121 BUG_ON(td->cpu != smp_processor_id());
122 ht->park(td->cpu);
123 td->status = HP_THREAD_PARKED;
124 }
125 kthread_parkme();
126 /* We might have been woken for stop */
127 continue;
128 }
129
130 BUG_ON(td->cpu != smp_processor_id());
131
132 /* Check for state change setup */
133 switch (td->status) {
134 case HP_THREAD_NONE:
135 preempt_enable();
136 if (ht->setup)
137 ht->setup(td->cpu);
138 td->status = HP_THREAD_ACTIVE;
139 preempt_disable();
140 break;
141 case HP_THREAD_PARKED:
142 preempt_enable();
143 if (ht->unpark)
144 ht->unpark(td->cpu);
145 td->status = HP_THREAD_ACTIVE;
146 preempt_disable();
147 break;
148 }
149
150 if (!ht->thread_should_run(td->cpu)) {
151 preempt_enable();
152 schedule();
153 } else {
154 set_current_state(TASK_RUNNING);
155 preempt_enable();
156 ht->thread_fn(td->cpu);
157 }
158 }
159}
160
161static int
162__smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
163{
164 struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
165 struct smpboot_thread_data *td;
166
167 if (tsk)
168 return 0;
169
170 td = kzalloc_node(sizeof(*td), GFP_KERNEL, cpu_to_node(cpu));
171 if (!td)
172 return -ENOMEM;
173 td->cpu = cpu;
174 td->ht = ht;
175
176 tsk = kthread_create_on_cpu(smpboot_thread_fn, td, cpu,
177 ht->thread_comm);
178 if (IS_ERR(tsk)) {
179 kfree(td);
180 return PTR_ERR(tsk);
181 }
182
183 get_task_struct(tsk);
184 *per_cpu_ptr(ht->store, cpu) = tsk;
185 return 0;
186}
187
188int smpboot_create_threads(unsigned int cpu)
189{
190 struct smp_hotplug_thread *cur;
191 int ret = 0;
192
193 mutex_lock(&smpboot_threads_lock);
194 list_for_each_entry(cur, &hotplug_threads, list) {
195 ret = __smpboot_create_thread(cur, cpu);
196 if (ret)
197 break;
198 }
199 mutex_unlock(&smpboot_threads_lock);
200 return ret;
201}
202
203static void smpboot_unpark_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
204{
205 struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
206
207 kthread_unpark(tsk);
208}
209
210void smpboot_unpark_threads(unsigned int cpu)
211{
212 struct smp_hotplug_thread *cur;
213
214 mutex_lock(&smpboot_threads_lock);
215 list_for_each_entry(cur, &hotplug_threads, list)
216 smpboot_unpark_thread(cur, cpu);
217 mutex_unlock(&smpboot_threads_lock);
218}
219
220static void smpboot_park_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
221{
222 struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
223
224 if (tsk)
225 kthread_park(tsk);
226}
227
228void smpboot_park_threads(unsigned int cpu)
229{
230 struct smp_hotplug_thread *cur;
231
232 mutex_lock(&smpboot_threads_lock);
233 list_for_each_entry_reverse(cur, &hotplug_threads, list)
234 smpboot_park_thread(cur, cpu);
235 mutex_unlock(&smpboot_threads_lock);
236}
237
238static void smpboot_destroy_threads(struct smp_hotplug_thread *ht)
239{
240 unsigned int cpu;
241
242 /* We need to destroy also the parked threads of offline cpus */
243 for_each_possible_cpu(cpu) {
244 struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
245
246 if (tsk) {
247 kthread_stop(tsk);
248 put_task_struct(tsk);
249 *per_cpu_ptr(ht->store, cpu) = NULL;
250 }
251 }
252}
253
254/**
255 * smpboot_register_percpu_thread - Register a per_cpu thread related to hotplug
256 * @plug_thread: Hotplug thread descriptor
257 *
258 * Creates and starts the threads on all online cpus.
259 */
260int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread)
261{
262 unsigned int cpu;
263 int ret = 0;
264
265 mutex_lock(&smpboot_threads_lock);
266 for_each_online_cpu(cpu) {
267 ret = __smpboot_create_thread(plug_thread, cpu);
268 if (ret) {
269 smpboot_destroy_threads(plug_thread);
270 goto out;
271 }
272 smpboot_unpark_thread(plug_thread, cpu);
273 }
274 list_add(&plug_thread->list, &hotplug_threads);
275out:
276 mutex_unlock(&smpboot_threads_lock);
277 return ret;
278}
279EXPORT_SYMBOL_GPL(smpboot_register_percpu_thread);
280
281/**
282 * smpboot_unregister_percpu_thread - Unregister a per_cpu thread related to hotplug
283 * @plug_thread: Hotplug thread descriptor
284 *
285 * Stops all threads on all possible cpus.
286 */
287void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread)
288{
289 get_online_cpus();
290 mutex_lock(&smpboot_threads_lock);
291 list_del(&plug_thread->list);
292 smpboot_destroy_threads(plug_thread);
293 mutex_unlock(&smpboot_threads_lock);
294 put_online_cpus();
295}
296EXPORT_SYMBOL_GPL(smpboot_unregister_percpu_thread);
diff --git a/kernel/smpboot.h b/kernel/smpboot.h
index 6ef9433e1c70..72415a0eb955 100644
--- a/kernel/smpboot.h
+++ b/kernel/smpboot.h
@@ -13,4 +13,8 @@ static inline void idle_thread_set_boot_cpu(void) { }
13static inline void idle_threads_init(void) { } 13static inline void idle_threads_init(void) { }
14#endif 14#endif
15 15
16int smpboot_create_threads(unsigned int cpu);
17void smpboot_park_threads(unsigned int cpu);
18void smpboot_unpark_threads(unsigned int cpu);
19
16#endif 20#endif