aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Morton <akpm@linux-foundation.org>2007-07-16 02:39:51 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-16 12:05:41 -0400
commitc67ad917cbf21b2862e2cf8e8b28339872ef7927 (patch)
tree362d8cf8668998b8cd5deb611693f584b8df5ee5
parent85420ccad1610f123365eec89848ef25641bc6b7 (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.h18
-rw-r--r--lib/percpu_counter.c66
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 @@
17struct percpu_counter { 18struct 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
29static inline void percpu_counter_init(struct percpu_counter *fbc, s64 amount) 33void percpu_counter_init(struct percpu_counter *fbc, s64 amount);
30{ 34void 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
36static inline void percpu_counter_destroy(struct percpu_counter *fbc)
37{
38 free_percpu(fbc->counters);
39}
40
41void percpu_counter_mod(struct percpu_counter *fbc, s32 amount); 35void percpu_counter_mod(struct percpu_counter *fbc, s32 amount);
42s64 percpu_counter_sum(struct percpu_counter *fbc); 36s64 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
13static LIST_HEAD(percpu_counters);
14static DEFINE_MUTEX(percpu_counters_lock);
15#endif
16
8void percpu_counter_mod(struct percpu_counter *fbc, s32 amount) 17void 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}
46EXPORT_SYMBOL(percpu_counter_sum); 55EXPORT_SYMBOL(percpu_counter_sum);
56
57void 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}
68EXPORT_SYMBOL(percpu_counter_init);
69
70void 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}
79EXPORT_SYMBOL(percpu_counter_destroy);
80
81#ifdef CONFIG_HOTPLUG_CPU
82static 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
106static int __init percpu_counter_startup(void)
107{
108 hotcpu_notifier(percpu_counter_hotcpu_callback, 0);
109 return 0;
110}
111module_init(percpu_counter_startup);
112#endif