blob: cf22c617baa44db8708f9908efc64b9f767e0c92 [file] [log] [blame]
Ravikiran G Thirumalai3cbc5642006-06-23 02:05:40 -07001/*
2 * Fast batching percpu counters.
3 */
4
5#include <linux/percpu_counter.h>
Andrew Mortonc67ad912007-07-15 23:39:51 -07006#include <linux/notifier.h>
7#include <linux/mutex.h>
8#include <linux/init.h>
9#include <linux/cpu.h>
Ravikiran G Thirumalai3cbc5642006-06-23 02:05:40 -070010#include <linux/module.h>
11
Andrew Mortonc67ad912007-07-15 23:39:51 -070012#ifdef CONFIG_HOTPLUG_CPU
13static LIST_HEAD(percpu_counters);
14static DEFINE_MUTEX(percpu_counters_lock);
15#endif
16
Mingming Cao0216bfc2006-06-23 02:05:41 -070017void percpu_counter_mod(struct percpu_counter *fbc, s32 amount)
Ravikiran G Thirumalai3cbc5642006-06-23 02:05:40 -070018{
19 long count;
Mingming Cao0216bfc2006-06-23 02:05:41 -070020 s32 *pcount;
Ravikiran G Thirumalai3cbc5642006-06-23 02:05:40 -070021 int cpu = get_cpu();
22
23 pcount = per_cpu_ptr(fbc->counters, cpu);
24 count = *pcount + amount;
25 if (count >= FBC_BATCH || count <= -FBC_BATCH) {
26 spin_lock(&fbc->lock);
27 fbc->count += count;
28 *pcount = 0;
29 spin_unlock(&fbc->lock);
30 } else {
31 *pcount = count;
32 }
33 put_cpu();
34}
35EXPORT_SYMBOL(percpu_counter_mod);
36
37/*
38 * Add up all the per-cpu counts, return the result. This is a more accurate
39 * but much slower version of percpu_counter_read_positive()
40 */
Mingming Cao0216bfc2006-06-23 02:05:41 -070041s64 percpu_counter_sum(struct percpu_counter *fbc)
Ravikiran G Thirumalai3cbc5642006-06-23 02:05:40 -070042{
Mingming Cao0216bfc2006-06-23 02:05:41 -070043 s64 ret;
Ravikiran G Thirumalai3cbc5642006-06-23 02:05:40 -070044 int cpu;
45
46 spin_lock(&fbc->lock);
47 ret = fbc->count;
Andrew Mortonb4ef0292007-07-15 23:39:51 -070048 for_each_online_cpu(cpu) {
Mingming Cao0216bfc2006-06-23 02:05:41 -070049 s32 *pcount = per_cpu_ptr(fbc->counters, cpu);
Ravikiran G Thirumalai3cbc5642006-06-23 02:05:40 -070050 ret += *pcount;
51 }
52 spin_unlock(&fbc->lock);
53 return ret < 0 ? 0 : ret;
54}
55EXPORT_SYMBOL(percpu_counter_sum);
Andrew Mortonc67ad912007-07-15 23:39:51 -070056
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