diff options
| author | Andrew Morton <akpm@linux-foundation.org> | 2007-07-16 02:39:51 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:41 -0400 |
| commit | c67ad917cbf21b2862e2cf8e8b28339872ef7927 (patch) | |
| tree | 362d8cf8668998b8cd5deb611693f584b8df5ee5 | |
| parent | 85420ccad1610f123365eec89848ef25641bc6b7 (diff) | |
percpu_counters(): use cpu notifiers
per-cpu counters presently must iterate over all possible CPUs in the
exhaustive percpu_counter_sum().
But it can be much better to only iterate over the presently-online CPUs. To
do this, we must arrange for an offlined CPU's count to be spilled into the
counter's central count.
We can do this for all percpu_counters in the machine by linking them into a
single global list and walking that list at CPU_DEAD time.
(I hope. Might have race windows in which the percpu_counter_sum() count is
inaccurate?)
Cc: Gautham R Shenoy <ego@in.ibm.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | include/linux/percpu_counter.h | 18 | ||||
| -rw-r--r-- | lib/percpu_counter.c | 66 |
2 files changed, 72 insertions, 12 deletions
diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index f5aa593ccf32..3d9f70972cdf 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include <linux/spinlock.h> | 9 | #include <linux/spinlock.h> |
| 10 | #include <linux/smp.h> | 10 | #include <linux/smp.h> |
| 11 | #include <linux/list.h> | ||
| 11 | #include <linux/threads.h> | 12 | #include <linux/threads.h> |
| 12 | #include <linux/percpu.h> | 13 | #include <linux/percpu.h> |
| 13 | #include <linux/types.h> | 14 | #include <linux/types.h> |
| @@ -17,6 +18,9 @@ | |||
| 17 | struct percpu_counter { | 18 | struct percpu_counter { |
| 18 | spinlock_t lock; | 19 | spinlock_t lock; |
| 19 | s64 count; | 20 | s64 count; |
| 21 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 22 | struct list_head list; /* All percpu_counters are on a list */ | ||
| 23 | #endif | ||
| 20 | s32 *counters; | 24 | s32 *counters; |
| 21 | }; | 25 | }; |
| 22 | 26 | ||
| @@ -26,18 +30,8 @@ struct percpu_counter { | |||
| 26 | #define FBC_BATCH (NR_CPUS*4) | 30 | #define FBC_BATCH (NR_CPUS*4) |
| 27 | #endif | 31 | #endif |
| 28 | 32 | ||
| 29 | static inline void percpu_counter_init(struct percpu_counter *fbc, s64 amount) | 33 | void percpu_counter_init(struct percpu_counter *fbc, s64 amount); |
| 30 | { | 34 | void percpu_counter_destroy(struct percpu_counter *fbc); |
| 31 | spin_lock_init(&fbc->lock); | ||
| 32 | fbc->count = amount; | ||
| 33 | fbc->counters = alloc_percpu(s32); | ||
| 34 | } | ||
| 35 | |||
| 36 | static inline void percpu_counter_destroy(struct percpu_counter *fbc) | ||
| 37 | { | ||
| 38 | free_percpu(fbc->counters); | ||
| 39 | } | ||
| 40 | |||
| 41 | void percpu_counter_mod(struct percpu_counter *fbc, s32 amount); | 35 | void percpu_counter_mod(struct percpu_counter *fbc, s32 amount); |
| 42 | s64 percpu_counter_sum(struct percpu_counter *fbc); | 36 | s64 percpu_counter_sum(struct percpu_counter *fbc); |
| 43 | 37 | ||
diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index 850449080e1c..8901c4e9c2e6 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c | |||
| @@ -3,8 +3,17 @@ | |||
| 3 | */ | 3 | */ |
| 4 | 4 | ||
| 5 | #include <linux/percpu_counter.h> | 5 | #include <linux/percpu_counter.h> |
| 6 | #include <linux/notifier.h> | ||
| 7 | #include <linux/mutex.h> | ||
| 8 | #include <linux/init.h> | ||
| 9 | #include <linux/cpu.h> | ||
| 6 | #include <linux/module.h> | 10 | #include <linux/module.h> |
| 7 | 11 | ||
| 12 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 13 | static LIST_HEAD(percpu_counters); | ||
| 14 | static DEFINE_MUTEX(percpu_counters_lock); | ||
| 15 | #endif | ||
| 16 | |||
| 8 | void percpu_counter_mod(struct percpu_counter *fbc, s32 amount) | 17 | void percpu_counter_mod(struct percpu_counter *fbc, s32 amount) |
| 9 | { | 18 | { |
| 10 | long count; | 19 | long count; |
| @@ -44,3 +53,60 @@ s64 percpu_counter_sum(struct percpu_counter *fbc) | |||
| 44 | return ret < 0 ? 0 : ret; | 53 | return ret < 0 ? 0 : ret; |
| 45 | } | 54 | } |
| 46 | EXPORT_SYMBOL(percpu_counter_sum); | 55 | EXPORT_SYMBOL(percpu_counter_sum); |
| 56 | |||
| 57 | void percpu_counter_init(struct percpu_counter *fbc, s64 amount) | ||
| 58 | { | ||
| 59 | spin_lock_init(&fbc->lock); | ||
| 60 | fbc->count = amount; | ||
| 61 | fbc->counters = alloc_percpu(s32); | ||
| 62 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 63 | mutex_lock(&percpu_counters_lock); | ||
| 64 | list_add(&fbc->list, &percpu_counters); | ||
| 65 | mutex_unlock(&percpu_counters_lock); | ||
| 66 | #endif | ||
| 67 | } | ||
| 68 | EXPORT_SYMBOL(percpu_counter_init); | ||
| 69 | |||
| 70 | void percpu_counter_destroy(struct percpu_counter *fbc) | ||
| 71 | { | ||
| 72 | free_percpu(fbc->counters); | ||
| 73 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 74 | mutex_lock(&percpu_counters_lock); | ||
| 75 | list_del(&fbc->list); | ||
| 76 | mutex_unlock(&percpu_counters_lock); | ||
| 77 | #endif | ||
| 78 | } | ||
| 79 | EXPORT_SYMBOL(percpu_counter_destroy); | ||
| 80 | |||
| 81 | #ifdef CONFIG_HOTPLUG_CPU | ||
| 82 | static int __cpuinit percpu_counter_hotcpu_callback(struct notifier_block *nb, | ||
| 83 | unsigned long action, void *hcpu) | ||
| 84 | { | ||
| 85 | unsigned int cpu; | ||
| 86 | struct percpu_counter *fbc; | ||
| 87 | |||
| 88 | if (action != CPU_DEAD) | ||
| 89 | return NOTIFY_OK; | ||
| 90 | |||
| 91 | cpu = (unsigned long)hcpu; | ||
| 92 | mutex_lock(&percpu_counters_lock); | ||
| 93 | list_for_each_entry(fbc, &percpu_counters, list) { | ||
| 94 | s32 *pcount; | ||
| 95 | |||
| 96 | spin_lock(&fbc->lock); | ||
| 97 | pcount = per_cpu_ptr(fbc->counters, cpu); | ||
| 98 | fbc->count += *pcount; | ||
| 99 | *pcount = 0; | ||
| 100 | spin_unlock(&fbc->lock); | ||
| 101 | } | ||
| 102 | mutex_unlock(&percpu_counters_lock); | ||
| 103 | return NOTIFY_OK; | ||
| 104 | } | ||
| 105 | |||
| 106 | static int __init percpu_counter_startup(void) | ||
| 107 | { | ||
| 108 | hotcpu_notifier(percpu_counter_hotcpu_callback, 0); | ||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | module_init(percpu_counter_startup); | ||
| 112 | #endif | ||
