Branch data Line data Source code
1 : : /*
2 : : * Fast batching percpu counters.
3 : : */
4 : :
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>
10 : : #include <linux/module.h>
11 : : #include <linux/debugobjects.h>
12 : :
13 : : #ifdef CONFIG_HOTPLUG_CPU
14 : : static LIST_HEAD(percpu_counters);
15 : : static DEFINE_SPINLOCK(percpu_counters_lock);
16 : : #endif
17 : :
18 : : #ifdef CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER
19 : :
20 : : static struct debug_obj_descr percpu_counter_debug_descr;
21 : :
22 : : static int percpu_counter_fixup_free(void *addr, enum debug_obj_state state)
23 : : {
24 : : struct percpu_counter *fbc = addr;
25 : :
26 : : switch (state) {
27 : : case ODEBUG_STATE_ACTIVE:
28 : : percpu_counter_destroy(fbc);
29 : : debug_object_free(fbc, &percpu_counter_debug_descr);
30 : : return 1;
31 : : default:
32 : : return 0;
33 : : }
34 : : }
35 : :
36 : : static struct debug_obj_descr percpu_counter_debug_descr = {
37 : : .name = "percpu_counter",
38 : : .fixup_free = percpu_counter_fixup_free,
39 : : };
40 : :
41 : : static inline void debug_percpu_counter_activate(struct percpu_counter *fbc)
42 : : {
43 : : debug_object_init(fbc, &percpu_counter_debug_descr);
44 : : debug_object_activate(fbc, &percpu_counter_debug_descr);
45 : : }
46 : :
47 : : static inline void debug_percpu_counter_deactivate(struct percpu_counter *fbc)
48 : : {
49 : : debug_object_deactivate(fbc, &percpu_counter_debug_descr);
50 : : debug_object_free(fbc, &percpu_counter_debug_descr);
51 : : }
52 : :
53 : : #else /* CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER */
54 : : static inline void debug_percpu_counter_activate(struct percpu_counter *fbc)
55 : : { }
56 : : static inline void debug_percpu_counter_deactivate(struct percpu_counter *fbc)
57 : : { }
58 : : #endif /* CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER */
59 : :
60 : 0 : void percpu_counter_set(struct percpu_counter *fbc, s64 amount)
61 : : {
62 : : int cpu;
63 : : unsigned long flags;
64 : :
65 : 0 : raw_spin_lock_irqsave(&fbc->lock, flags);
66 [ # # ]: 0 : for_each_possible_cpu(cpu) {
67 : 0 : s32 *pcount = per_cpu_ptr(fbc->counters, cpu);
68 : 0 : *pcount = 0;
69 : : }
70 : 0 : fbc->count = amount;
71 : 0 : raw_spin_unlock_irqrestore(&fbc->lock, flags);
72 : 0 : }
73 : : EXPORT_SYMBOL(percpu_counter_set);
74 : :
75 : 0 : void __percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch)
76 : : {
77 : : s64 count;
78 : :
79 : 72840793 : preempt_disable();
80 : 145683734 : count = __this_cpu_read(*fbc->counters) + amount;
81 [ + + ][ + + ]: 73951444 : if (count >= batch || count <= -batch) {
82 : : unsigned long flags;
83 : 1111503 : raw_spin_lock_irqsave(&fbc->lock, flags);
84 : 1109576 : fbc->count += count;
85 : 2219152 : __this_cpu_sub(*fbc->counters, count - amount);
86 : 1109576 : raw_spin_unlock_irqrestore(&fbc->lock, flags);
87 : : } else {
88 : 143477462 : this_cpu_add(*fbc->counters, amount);
89 : : }
90 : 72864607 : preempt_enable();
91 : 72863564 : }
92 : : EXPORT_SYMBOL(__percpu_counter_add);
93 : :
94 : : /*
95 : : * Add up all the per-cpu counts, return the result. This is a more accurate
96 : : * but much slower version of percpu_counter_read_positive()
97 : : */
98 : 0 : s64 __percpu_counter_sum(struct percpu_counter *fbc)
99 : : {
100 : : s64 ret;
101 : : int cpu;
102 : : unsigned long flags;
103 : :
104 : 64244 : raw_spin_lock_irqsave(&fbc->lock, flags);
105 : 64244 : ret = fbc->count;
106 [ + + ]: 256976 : for_each_online_cpu(cpu) {
107 : 128488 : s32 *pcount = per_cpu_ptr(fbc->counters, cpu);
108 : 128488 : ret += *pcount;
109 : : }
110 : 64244 : raw_spin_unlock_irqrestore(&fbc->lock, flags);
111 : 64244 : return ret;
112 : : }
113 : : EXPORT_SYMBOL(__percpu_counter_sum);
114 : :
115 : 0 : int __percpu_counter_init(struct percpu_counter *fbc, s64 amount,
116 : : struct lock_class_key *key)
117 : : {
118 : 9 : raw_spin_lock_init(&fbc->lock);
119 : : lockdep_set_class(&fbc->lock, key);
120 : 9 : fbc->count = amount;
121 : 9 : fbc->counters = alloc_percpu(s32);
122 [ + - ]: 9 : if (!fbc->counters)
123 : : return -ENOMEM;
124 : :
125 : : debug_percpu_counter_activate(fbc);
126 : :
127 : : #ifdef CONFIG_HOTPLUG_CPU
128 : 9 : INIT_LIST_HEAD(&fbc->list);
129 : : spin_lock(&percpu_counters_lock);
130 : : list_add(&fbc->list, &percpu_counters);
131 : : spin_unlock(&percpu_counters_lock);
132 : : #endif
133 : 9 : return 0;
134 : : }
135 : : EXPORT_SYMBOL(__percpu_counter_init);
136 : :
137 : 0 : void percpu_counter_destroy(struct percpu_counter *fbc)
138 : : {
139 [ + - ]: 9 : if (!fbc->counters)
140 : 0 : return;
141 : :
142 : : debug_percpu_counter_deactivate(fbc);
143 : :
144 : : #ifdef CONFIG_HOTPLUG_CPU
145 : : spin_lock(&percpu_counters_lock);
146 : : list_del(&fbc->list);
147 : : spin_unlock(&percpu_counters_lock);
148 : : #endif
149 : 9 : free_percpu(fbc->counters);
150 : 9 : fbc->counters = NULL;
151 : : }
152 : : EXPORT_SYMBOL(percpu_counter_destroy);
153 : :
154 : : int percpu_counter_batch __read_mostly = 32;
155 : : EXPORT_SYMBOL(percpu_counter_batch);
156 : :
157 : 0 : static void compute_batch_value(void)
158 : : {
159 : 0 : int nr = num_online_cpus();
160 : :
161 : 0 : percpu_counter_batch = max(32, nr*2);
162 : 0 : }
163 : :
164 : 0 : static int percpu_counter_hotcpu_callback(struct notifier_block *nb,
165 : : unsigned long action, void *hcpu)
166 : : {
167 : : #ifdef CONFIG_HOTPLUG_CPU
168 : : unsigned int cpu;
169 : : struct percpu_counter *fbc;
170 : :
171 : 0 : compute_batch_value();
172 [ # # ]: 0 : if (action != CPU_DEAD)
173 : : return NOTIFY_OK;
174 : :
175 : 0 : cpu = (unsigned long)hcpu;
176 : : spin_lock(&percpu_counters_lock);
177 [ # # ]: 0 : list_for_each_entry(fbc, &percpu_counters, list) {
178 : : s32 *pcount;
179 : : unsigned long flags;
180 : :
181 : 0 : raw_spin_lock_irqsave(&fbc->lock, flags);
182 : 0 : pcount = per_cpu_ptr(fbc->counters, cpu);
183 : 0 : fbc->count += *pcount;
184 : 0 : *pcount = 0;
185 : 0 : raw_spin_unlock_irqrestore(&fbc->lock, flags);
186 : : }
187 : : spin_unlock(&percpu_counters_lock);
188 : : #endif
189 : 0 : return NOTIFY_OK;
190 : : }
191 : :
192 : : /*
193 : : * Compare counter against given value.
194 : : * Return 1 if greater, 0 if equal and -1 if less
195 : : */
196 : 0 : int percpu_counter_compare(struct percpu_counter *fbc, s64 rhs)
197 : : {
198 : : s64 count;
199 : :
200 : : count = percpu_counter_read(fbc);
201 : : /* Check to see if rough count will be sufficient for comparison */
202 [ + - ]: 1147 : if (abs(count - rhs) > (percpu_counter_batch*num_online_cpus())) {
203 [ + - ]: 1147 : if (count > rhs)
204 : : return 1;
205 : : else
206 : 1147 : return -1;
207 : : }
208 : : /* Need to use precise count */
209 : : count = percpu_counter_sum(fbc);
210 [ # # ]: 0 : if (count > rhs)
211 : : return 1;
212 [ # # ]: 0 : else if (count < rhs)
213 : : return -1;
214 : : else
215 : 0 : return 0;
216 : : }
217 : : EXPORT_SYMBOL(percpu_counter_compare);
218 : :
219 : 0 : static int __init percpu_counter_startup(void)
220 : : {
221 : 0 : compute_batch_value();
222 : 0 : hotcpu_notifier(percpu_counter_hotcpu_callback, 0);
223 : 0 : return 0;
224 : : }
225 : : module_init(percpu_counter_startup);
|