aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/stop_machine.c
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2008-10-13 17:50:10 -0400
committerRusty Russell <rusty@rustcorp.com.au>2008-10-21 19:00:26 -0400
commitc9583e55fa2b08a230c549bd1e3c0bde6c50d9cc (patch)
treeec535aced7cdf7390290a4bf61402346b166c07b /kernel/stop_machine.c
parent0d557dc97f4bb501f086a03d0f00b99a7855d794 (diff)
stop_machine: use workqueues instead of kernel threads
Convert stop_machine to a workqueue based approach. Instead of using kernel threads for stop_machine we now use a an rt workqueue to synchronize all cpus. This has the advantage that all needed per cpu threads are already created when stop_machine gets called. And therefore a call to stop_machine won't fail anymore. This is needed for s390 which needs a mechanism to synchronize all cpus without allocating any memory. As Rusty pointed out free_module() needs a non-failing stop_machine interface as well. As a side effect the stop_machine code gets simplified. Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'kernel/stop_machine.c')
-rw-r--r--kernel/stop_machine.c111
1 files changed, 41 insertions, 70 deletions
diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c
index af3c7cea258..0e688c6a1a6 100644
--- a/kernel/stop_machine.c
+++ b/kernel/stop_machine.c
@@ -37,9 +37,13 @@ struct stop_machine_data {
37/* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */ 37/* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */
38static unsigned int num_threads; 38static unsigned int num_threads;
39static atomic_t thread_ack; 39static atomic_t thread_ack;
40static struct completion finished;
41static DEFINE_MUTEX(lock); 40static DEFINE_MUTEX(lock);
42 41
42static struct workqueue_struct *stop_machine_wq;
43static struct stop_machine_data active, idle;
44static const cpumask_t *active_cpus;
45static void *stop_machine_work;
46
43static void set_state(enum stopmachine_state newstate) 47static void set_state(enum stopmachine_state newstate)
44{ 48{
45 /* Reset ack counter. */ 49 /* Reset ack counter. */
@@ -51,21 +55,25 @@ static void set_state(enum stopmachine_state newstate)
51/* Last one to ack a state moves to the next state. */ 55/* Last one to ack a state moves to the next state. */
52static void ack_state(void) 56static void ack_state(void)
53{ 57{
54 if (atomic_dec_and_test(&thread_ack)) { 58 if (atomic_dec_and_test(&thread_ack))
55 /* If we're the last one to ack the EXIT, we're finished. */ 59 set_state(state + 1);
56 if (state == STOPMACHINE_EXIT)
57 complete(&finished);
58 else
59 set_state(state + 1);
60 }
61} 60}
62 61
63/* This is the actual thread which stops the CPU. It exits by itself rather 62/* This is the actual function which stops the CPU. It runs
64 * than waiting for kthread_stop(), because it's easier for hotplug CPU. */ 63 * in the context of a dedicated stopmachine workqueue. */
65static int stop_cpu(struct stop_machine_data *smdata) 64static void stop_cpu(struct work_struct *unused)
66{ 65{
67 enum stopmachine_state curstate = STOPMACHINE_NONE; 66 enum stopmachine_state curstate = STOPMACHINE_NONE;
68 67 struct stop_machine_data *smdata = &idle;
68 int cpu = smp_processor_id();
69
70 if (!active_cpus) {
71 if (cpu == first_cpu(cpu_online_map))
72 smdata = &active;
73 } else {
74 if (cpu_isset(cpu, *active_cpus))
75 smdata = &active;
76 }
69 /* Simple state machine */ 77 /* Simple state machine */
70 do { 78 do {
71 /* Chill out and ensure we re-read stopmachine_state. */ 79 /* Chill out and ensure we re-read stopmachine_state. */
@@ -90,7 +98,6 @@ static int stop_cpu(struct stop_machine_data *smdata)
90 } while (curstate != STOPMACHINE_EXIT); 98 } while (curstate != STOPMACHINE_EXIT);
91 99
92 local_irq_enable(); 100 local_irq_enable();
93 do_exit(0);
94} 101}
95 102
96/* Callback for CPUs which aren't supposed to do anything. */ 103/* Callback for CPUs which aren't supposed to do anything. */
@@ -101,78 +108,34 @@ static int chill(void *unused)
101 108
102int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) 109int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus)
103{ 110{
104 int i, err; 111 struct work_struct *sm_work;
105 struct stop_machine_data active, idle; 112 int i;
106 struct task_struct **threads;
107 113
114 /* Set up initial state. */
115 mutex_lock(&lock);
116 num_threads = num_online_cpus();
117 active_cpus = cpus;
108 active.fn = fn; 118 active.fn = fn;
109 active.data = data; 119 active.data = data;
110 active.fnret = 0; 120 active.fnret = 0;
111 idle.fn = chill; 121 idle.fn = chill;
112 idle.data = NULL; 122 idle.data = NULL;
113 123
114 /* This could be too big for stack on large machines. */
115 threads = kcalloc(NR_CPUS, sizeof(threads[0]), GFP_KERNEL);
116 if (!threads)
117 return -ENOMEM;
118
119 /* Set up initial state. */
120 mutex_lock(&lock);
121 init_completion(&finished);
122 num_threads = num_online_cpus();
123 set_state(STOPMACHINE_PREPARE); 124 set_state(STOPMACHINE_PREPARE);
124 125
125 for_each_online_cpu(i) { 126 /* Schedule the stop_cpu work on all cpus: hold this CPU so one
126 struct stop_machine_data *smdata = &idle;
127 struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
128
129 if (!cpus) {
130 if (i == first_cpu(cpu_online_map))
131 smdata = &active;
132 } else {
133 if (cpu_isset(i, *cpus))
134 smdata = &active;
135 }
136
137 threads[i] = kthread_create((void *)stop_cpu, smdata, "kstop%u",
138 i);
139 if (IS_ERR(threads[i])) {
140 err = PTR_ERR(threads[i]);
141 threads[i] = NULL;
142 goto kill_threads;
143 }
144
145 /* Place it onto correct cpu. */
146 kthread_bind(threads[i], i);
147
148 /* Make it highest prio. */
149 if (sched_setscheduler_nocheck(threads[i], SCHED_FIFO, &param))
150 BUG();
151 }
152
153 /* We've created all the threads. Wake them all: hold this CPU so one
154 * doesn't hit this CPU until we're ready. */ 127 * doesn't hit this CPU until we're ready. */
155 get_cpu(); 128 get_cpu();
156 for_each_online_cpu(i) 129 for_each_online_cpu(i) {
157 wake_up_process(threads[i]); 130 sm_work = percpu_ptr(stop_machine_work, i);
158 131 INIT_WORK(sm_work, stop_cpu);
132 queue_work_on(i, stop_machine_wq, sm_work);
133 }
159 /* This will release the thread on our CPU. */ 134 /* This will release the thread on our CPU. */
160 put_cpu(); 135 put_cpu();
161 wait_for_completion(&finished); 136 flush_workqueue(stop_machine_wq);
162 mutex_unlock(&lock); 137 mutex_unlock(&lock);
163
164 kfree(threads);
165
166 return active.fnret; 138 return active.fnret;
167
168kill_threads:
169 for_each_online_cpu(i)
170 if (threads[i])
171 kthread_stop(threads[i]);
172 mutex_unlock(&lock);
173
174 kfree(threads);
175 return err;
176} 139}
177 140
178int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) 141int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus)
@@ -187,3 +150,11 @@ int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus)
187 return ret; 150 return ret;
188} 151}
189EXPORT_SYMBOL_GPL(stop_machine); 152EXPORT_SYMBOL_GPL(stop_machine);
153
154static int __init stop_machine_init(void)
155{
156 stop_machine_wq = create_rt_workqueue("kstop");
157 stop_machine_work = alloc_percpu(struct work_struct);
158 return 0;
159}
160early_initcall(stop_machine_init);