Skip to content

Commit 8053871

Browse files
torvaldsIngo Molnar
authored andcommitted
smp: Fix smp_call_function_single_async() locking
The current smp_function_call code suffers a number of problems, most notably smp_call_function_single_async() is broken. The problem is that flush_smp_call_function_queue() does csd_unlock() _after_ calling csd->func(). This means that a caller cannot properly synchronize the csd usage as it has to. Change the code to release the csd before calling ->func() for the async case, and put a WARN_ON_ONCE(csd->flags & CSD_FLAG_LOCK) in smp_call_function_single_async() to warn us of improper serialization, because any waiting there can results in deadlocks when called with IRQs disabled. Rename the (currently) unused WAIT flag to SYNCHRONOUS and (re)use it such that we know what to do in flush_smp_call_function_queue(). Rework csd_{,un}lock() to use smp_load_acquire() / smp_store_release() to avoid some full barriers while more clearly providing lock semantics. Finally move the csd maintenance out of generic_exec_single() into its callers for clearer code. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> [ Added changelog. ] Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Jens Axboe <axboe@kernel.dk> Cc: Rafael David Tinoco <inaddy@ubuntu.com> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/CA+55aFz492bzLFhdbKN-Hygjcreup7CjMEYk3nTSfRWjppz-OA@mail.gmail.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent d7bc319 commit 8053871

File tree

1 file changed

+47
-31
lines changed

1 file changed

+47
-31
lines changed

kernel/smp.c

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
enum {
2121
CSD_FLAG_LOCK = 0x01,
22-
CSD_FLAG_WAIT = 0x02,
22+
CSD_FLAG_SYNCHRONOUS = 0x02,
2323
};
2424

2525
struct call_function_data {
@@ -107,7 +107,7 @@ void __init call_function_init(void)
107107
*/
108108
static void csd_lock_wait(struct call_single_data *csd)
109109
{
110-
while (csd->flags & CSD_FLAG_LOCK)
110+
while (smp_load_acquire(&csd->flags) & CSD_FLAG_LOCK)
111111
cpu_relax();
112112
}
113113

@@ -121,19 +121,17 @@ static void csd_lock(struct call_single_data *csd)
121121
* to ->flags with any subsequent assignments to other
122122
* fields of the specified call_single_data structure:
123123
*/
124-
smp_mb();
124+
smp_wmb();
125125
}
126126

127127
static void csd_unlock(struct call_single_data *csd)
128128
{
129-
WARN_ON((csd->flags & CSD_FLAG_WAIT) && !(csd->flags & CSD_FLAG_LOCK));
129+
WARN_ON(!(csd->flags & CSD_FLAG_LOCK));
130130

131131
/*
132132
* ensure we're all done before releasing data:
133133
*/
134-
smp_mb();
135-
136-
csd->flags &= ~CSD_FLAG_LOCK;
134+
smp_store_release(&csd->flags, 0);
137135
}
138136

139137
static DEFINE_PER_CPU_SHARED_ALIGNED(struct call_single_data, csd_data);
@@ -144,13 +142,16 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(struct call_single_data, csd_data);
144142
* ->func, ->info, and ->flags set.
145143
*/
146144
static int generic_exec_single(int cpu, struct call_single_data *csd,
147-
smp_call_func_t func, void *info, int wait)
145+
smp_call_func_t func, void *info)
148146
{
149-
struct call_single_data csd_stack = { .flags = 0 };
150-
unsigned long flags;
151-
152-
153147
if (cpu == smp_processor_id()) {
148+
unsigned long flags;
149+
150+
/*
151+
* We can unlock early even for the synchronous on-stack case,
152+
* since we're doing this from the same CPU..
153+
*/
154+
csd_unlock(csd);
154155
local_irq_save(flags);
155156
func(info);
156157
local_irq_restore(flags);
@@ -161,21 +162,9 @@ static int generic_exec_single(int cpu, struct call_single_data *csd,
161162
if ((unsigned)cpu >= nr_cpu_ids || !cpu_online(cpu))
162163
return -ENXIO;
163164

164-
165-
if (!csd) {
166-
csd = &csd_stack;
167-
if (!wait)
168-
csd = this_cpu_ptr(&csd_data);
169-
}
170-
171-
csd_lock(csd);
172-
173165
csd->func = func;
174166
csd->info = info;
175167

176-
if (wait)
177-
csd->flags |= CSD_FLAG_WAIT;
178-
179168
/*
180169
* The list addition should be visible before sending the IPI
181170
* handler locks the list to pull the entry off it because of
@@ -190,9 +179,6 @@ static int generic_exec_single(int cpu, struct call_single_data *csd,
190179
if (llist_add(&csd->llist, &per_cpu(call_single_queue, cpu)))
191180
arch_send_call_function_single_ipi(cpu);
192181

193-
if (wait)
194-
csd_lock_wait(csd);
195-
196182
return 0;
197183
}
198184

@@ -250,8 +236,17 @@ static void flush_smp_call_function_queue(bool warn_cpu_offline)
250236
}
251237

252238
llist_for_each_entry_safe(csd, csd_next, entry, llist) {
253-
csd->func(csd->info);
254-
csd_unlock(csd);
239+
smp_call_func_t func = csd->func;
240+
void *info = csd->info;
241+
242+
/* Do we wait until *after* callback? */
243+
if (csd->flags & CSD_FLAG_SYNCHRONOUS) {
244+
func(info);
245+
csd_unlock(csd);
246+
} else {
247+
csd_unlock(csd);
248+
func(info);
249+
}
255250
}
256251

257252
/*
@@ -274,6 +269,8 @@ static void flush_smp_call_function_queue(bool warn_cpu_offline)
274269
int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
275270
int wait)
276271
{
272+
struct call_single_data *csd;
273+
struct call_single_data csd_stack = { .flags = CSD_FLAG_LOCK | CSD_FLAG_SYNCHRONOUS };
277274
int this_cpu;
278275
int err;
279276

@@ -292,7 +289,16 @@ int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
292289
WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled()
293290
&& !oops_in_progress);
294291

295-
err = generic_exec_single(cpu, NULL, func, info, wait);
292+
csd = &csd_stack;
293+
if (!wait) {
294+
csd = this_cpu_ptr(&csd_data);
295+
csd_lock(csd);
296+
}
297+
298+
err = generic_exec_single(cpu, csd, func, info);
299+
300+
if (wait)
301+
csd_lock_wait(csd);
296302

297303
put_cpu();
298304

@@ -321,7 +327,15 @@ int smp_call_function_single_async(int cpu, struct call_single_data *csd)
321327
int err = 0;
322328

323329
preempt_disable();
324-
err = generic_exec_single(cpu, csd, csd->func, csd->info, 0);
330+
331+
/* We could deadlock if we have to wait here with interrupts disabled! */
332+
if (WARN_ON_ONCE(csd->flags & CSD_FLAG_LOCK))
333+
csd_lock_wait(csd);
334+
335+
csd->flags = CSD_FLAG_LOCK;
336+
smp_wmb();
337+
338+
err = generic_exec_single(cpu, csd, csd->func, csd->info);
325339
preempt_enable();
326340

327341
return err;
@@ -433,6 +447,8 @@ void smp_call_function_many(const struct cpumask *mask,
433447
struct call_single_data *csd = per_cpu_ptr(cfd->csd, cpu);
434448

435449
csd_lock(csd);
450+
if (wait)
451+
csd->flags |= CSD_FLAG_SYNCHRONOUS;
436452
csd->func = func;
437453
csd->info = info;
438454
llist_add(&csd->llist, &per_cpu(call_single_queue, cpu));

0 commit comments

Comments
 (0)