diff options
author | Xiao Guangrong <xiaoguangrong@cn.fujitsu.com> | 2009-09-22 19:43:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 10:39:28 -0400 |
commit | 54fdade1c3332391948ec43530c02c4794a38172 (patch) | |
tree | a44cfa6888bbe702321e4d4737786e5292d72eaa /kernel/smp.c | |
parent | 5c725138437837291db5c25f4a076ee852e806e3 (diff) |
generic-ipi: make struct call_function_data lockless
This patch can remove spinlock from struct call_function_data, the
reasons are below:
1: add a new interface for cpumask named cpumask_test_and_clear_cpu(),
it can atomically test and clear specific cpu, we can use it instead
of cpumask_test_cpu() and cpumask_clear_cpu() and no need data->lock
to protect those in generic_smp_call_function_interrupt().
2: in smp_call_function_many(), after csd_lock() return, the current's
cfd_data is deleted from call_function list, so it not have race
between other cpus, then cfs_data is only used in
smp_call_function_many() that must disable preemption and not from
a hardware interrupthandler or from a bottom half handler to call,
only the correspond cpu can use it, so it not have race in current
cpu, no need cfs_data->lock to protect it.
3: after 1 and 2, cfs_data->lock is only use to protect cfs_data->refs in
generic_smp_call_function_interrupt(), so we can define cfs_data->refs
to atomic_t, and no need cfs_data->lock any more.
Signed-off-by: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Jens Axboe <jens.axboe@oracle.com>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Cc: Peter Zijlstra <peterz@infradead.org>
Acked-by: Rusty Russell <rusty@rustcorp.com.au>
[akpm@linux-foundation.org: use atomic_dec_return()]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/smp.c')
-rw-r--r-- | kernel/smp.c | 29 |
1 files changed, 8 insertions, 21 deletions
diff --git a/kernel/smp.c b/kernel/smp.c index 8e218500ab14..fd47a256a24e 100644 --- a/kernel/smp.c +++ b/kernel/smp.c | |||
@@ -29,8 +29,7 @@ enum { | |||
29 | 29 | ||
30 | struct call_function_data { | 30 | struct call_function_data { |
31 | struct call_single_data csd; | 31 | struct call_single_data csd; |
32 | spinlock_t lock; | 32 | atomic_t refs; |
33 | unsigned int refs; | ||
34 | cpumask_var_t cpumask; | 33 | cpumask_var_t cpumask; |
35 | }; | 34 | }; |
36 | 35 | ||
@@ -39,9 +38,7 @@ struct call_single_queue { | |||
39 | spinlock_t lock; | 38 | spinlock_t lock; |
40 | }; | 39 | }; |
41 | 40 | ||
42 | static DEFINE_PER_CPU(struct call_function_data, cfd_data) = { | 41 | static DEFINE_PER_CPU(struct call_function_data, cfd_data); |
43 | .lock = __SPIN_LOCK_UNLOCKED(cfd_data.lock), | ||
44 | }; | ||
45 | 42 | ||
46 | static int | 43 | static int |
47 | hotplug_cfd(struct notifier_block *nfb, unsigned long action, void *hcpu) | 44 | hotplug_cfd(struct notifier_block *nfb, unsigned long action, void *hcpu) |
@@ -196,25 +193,18 @@ void generic_smp_call_function_interrupt(void) | |||
196 | list_for_each_entry_rcu(data, &call_function.queue, csd.list) { | 193 | list_for_each_entry_rcu(data, &call_function.queue, csd.list) { |
197 | int refs; | 194 | int refs; |
198 | 195 | ||
199 | spin_lock(&data->lock); | 196 | if (!cpumask_test_and_clear_cpu(cpu, data->cpumask)) |
200 | if (!cpumask_test_cpu(cpu, data->cpumask)) { | ||
201 | spin_unlock(&data->lock); | ||
202 | continue; | 197 | continue; |
203 | } | ||
204 | cpumask_clear_cpu(cpu, data->cpumask); | ||
205 | spin_unlock(&data->lock); | ||
206 | 198 | ||
207 | data->csd.func(data->csd.info); | 199 | data->csd.func(data->csd.info); |
208 | 200 | ||
209 | spin_lock(&data->lock); | 201 | refs = atomic_dec_return(&data->refs); |
210 | WARN_ON(data->refs == 0); | 202 | WARN_ON(refs < 0); |
211 | refs = --data->refs; | ||
212 | if (!refs) { | 203 | if (!refs) { |
213 | spin_lock(&call_function.lock); | 204 | spin_lock(&call_function.lock); |
214 | list_del_rcu(&data->csd.list); | 205 | list_del_rcu(&data->csd.list); |
215 | spin_unlock(&call_function.lock); | 206 | spin_unlock(&call_function.lock); |
216 | } | 207 | } |
217 | spin_unlock(&data->lock); | ||
218 | 208 | ||
219 | if (refs) | 209 | if (refs) |
220 | continue; | 210 | continue; |
@@ -419,23 +409,20 @@ void smp_call_function_many(const struct cpumask *mask, | |||
419 | data = &__get_cpu_var(cfd_data); | 409 | data = &__get_cpu_var(cfd_data); |
420 | csd_lock(&data->csd); | 410 | csd_lock(&data->csd); |
421 | 411 | ||
422 | spin_lock_irqsave(&data->lock, flags); | ||
423 | data->csd.func = func; | 412 | data->csd.func = func; |
424 | data->csd.info = info; | 413 | data->csd.info = info; |
425 | cpumask_and(data->cpumask, mask, cpu_online_mask); | 414 | cpumask_and(data->cpumask, mask, cpu_online_mask); |
426 | cpumask_clear_cpu(this_cpu, data->cpumask); | 415 | cpumask_clear_cpu(this_cpu, data->cpumask); |
427 | data->refs = cpumask_weight(data->cpumask); | 416 | atomic_set(&data->refs, cpumask_weight(data->cpumask)); |
428 | 417 | ||
429 | spin_lock(&call_function.lock); | 418 | spin_lock_irqsave(&call_function.lock, flags); |
430 | /* | 419 | /* |
431 | * Place entry at the _HEAD_ of the list, so that any cpu still | 420 | * Place entry at the _HEAD_ of the list, so that any cpu still |
432 | * observing the entry in generic_smp_call_function_interrupt() | 421 | * observing the entry in generic_smp_call_function_interrupt() |
433 | * will not miss any other list entries: | 422 | * will not miss any other list entries: |
434 | */ | 423 | */ |
435 | list_add_rcu(&data->csd.list, &call_function.queue); | 424 | list_add_rcu(&data->csd.list, &call_function.queue); |
436 | spin_unlock(&call_function.lock); | 425 | spin_unlock_irqrestore(&call_function.lock, flags); |
437 | |||
438 | spin_unlock_irqrestore(&data->lock, flags); | ||
439 | 426 | ||
440 | /* | 427 | /* |
441 | * Make the list addition visible before sending the ipi. | 428 | * Make the list addition visible before sending the ipi. |