1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
21da177e4SLinus Torvalds #ifndef _LINUX_PERCPU_COUNTER_H
31da177e4SLinus Torvalds #define _LINUX_PERCPU_COUNTER_H
41da177e4SLinus Torvalds /*
51da177e4SLinus Torvalds * A simple "approximate counter" for use in ext2 and ext3 superblocks.
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * WARNING: these things are HUGE. 4 kbytes per counter on 32-way P4.
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds #include <linux/spinlock.h>
111da177e4SLinus Torvalds #include <linux/smp.h>
12c67ad917SAndrew Morton #include <linux/list.h>
131da177e4SLinus Torvalds #include <linux/threads.h>
141da177e4SLinus Torvalds #include <linux/percpu.h>
150216bfcfSMingming Cao #include <linux/types.h>
161da177e4SLinus Torvalds
175d0ce359SJiebin Sun /* percpu_counter batch for local add or sub */
185d0ce359SJiebin Sun #define PERCPU_COUNTER_LOCAL_BATCH INT_MAX
195d0ce359SJiebin Sun
201da177e4SLinus Torvalds #ifdef CONFIG_SMP
211da177e4SLinus Torvalds
221da177e4SLinus Torvalds struct percpu_counter {
23f032a450SThomas Gleixner raw_spinlock_t lock;
240216bfcfSMingming Cao s64 count;
25c67ad917SAndrew Morton #ifdef CONFIG_HOTPLUG_CPU
26c67ad917SAndrew Morton struct list_head list; /* All percpu_counters are on a list */
27c67ad917SAndrew Morton #endif
2843cf38ebSTejun Heo s32 __percpu *counters;
291da177e4SLinus Torvalds };
301da177e4SLinus Torvalds
31179f7ebfSEric Dumazet extern int percpu_counter_batch;
321da177e4SLinus Torvalds
33c439d5e8SMateusz Guzik int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount,
34c439d5e8SMateusz Guzik gfp_t gfp, u32 nr_counters,
35ea319518SPeter Zijlstra struct lock_class_key *key);
36ea319518SPeter Zijlstra
37c439d5e8SMateusz Guzik #define percpu_counter_init_many(fbc, value, gfp, nr_counters) \
38ea319518SPeter Zijlstra ({ \
39ea319518SPeter Zijlstra static struct lock_class_key __key; \
40ea319518SPeter Zijlstra \
41c439d5e8SMateusz Guzik __percpu_counter_init_many(fbc, value, gfp, nr_counters,\
42c439d5e8SMateusz Guzik &__key); \
43ea319518SPeter Zijlstra })
44ea319518SPeter Zijlstra
45c439d5e8SMateusz Guzik
46c439d5e8SMateusz Guzik #define percpu_counter_init(fbc, value, gfp) \
47c439d5e8SMateusz Guzik percpu_counter_init_many(fbc, value, gfp, 1)
48c439d5e8SMateusz Guzik
49c439d5e8SMateusz Guzik void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters);
percpu_counter_destroy(struct percpu_counter * fbc)50c439d5e8SMateusz Guzik static inline void percpu_counter_destroy(struct percpu_counter *fbc)
51c439d5e8SMateusz Guzik {
52c439d5e8SMateusz Guzik percpu_counter_destroy_many(fbc, 1);
53c439d5e8SMateusz Guzik }
54c439d5e8SMateusz Guzik
553a587f47SPeter Zijlstra void percpu_counter_set(struct percpu_counter *fbc, s64 amount);
56104b4e51SNikolay Borisov void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount,
57104b4e51SNikolay Borisov s32 batch);
5802d21168SAndrew Morton s64 __percpu_counter_sum(struct percpu_counter *fbc);
5980188b0dSDave Chinner int __percpu_counter_compare(struct percpu_counter *fbc, s64 rhs, s32 batch);
60beb98686SHugh Dickins bool __percpu_counter_limited_add(struct percpu_counter *fbc, s64 limit,
61beb98686SHugh Dickins s64 amount, s32 batch);
620a4954a8SFeng Tang void percpu_counter_sync(struct percpu_counter *fbc);
6380188b0dSDave Chinner
percpu_counter_compare(struct percpu_counter * fbc,s64 rhs)6480188b0dSDave Chinner static inline int percpu_counter_compare(struct percpu_counter *fbc, s64 rhs)
6580188b0dSDave Chinner {
6680188b0dSDave Chinner return __percpu_counter_compare(fbc, rhs, percpu_counter_batch);
6780188b0dSDave Chinner }
681da177e4SLinus Torvalds
percpu_counter_add(struct percpu_counter * fbc,s64 amount)6920e89767SPeter Zijlstra static inline void percpu_counter_add(struct percpu_counter *fbc, s64 amount)
70252e0ba6SPeter Zijlstra {
71104b4e51SNikolay Borisov percpu_counter_add_batch(fbc, amount, percpu_counter_batch);
72252e0ba6SPeter Zijlstra }
73252e0ba6SPeter Zijlstra
74beb98686SHugh Dickins static inline bool
percpu_counter_limited_add(struct percpu_counter * fbc,s64 limit,s64 amount)75beb98686SHugh Dickins percpu_counter_limited_add(struct percpu_counter *fbc, s64 limit, s64 amount)
76beb98686SHugh Dickins {
77beb98686SHugh Dickins return __percpu_counter_limited_add(fbc, limit, amount,
78beb98686SHugh Dickins percpu_counter_batch);
79beb98686SHugh Dickins }
80beb98686SHugh Dickins
815d0ce359SJiebin Sun /*
825d0ce359SJiebin Sun * With percpu_counter_add_local() and percpu_counter_sub_local(), counts
835d0ce359SJiebin Sun * are accumulated in local per cpu counter and not in fbc->count until
845d0ce359SJiebin Sun * local count overflows PERCPU_COUNTER_LOCAL_BATCH. This makes counter
855d0ce359SJiebin Sun * write efficient.
865d0ce359SJiebin Sun * But percpu_counter_sum(), instead of percpu_counter_read(), needs to be
875d0ce359SJiebin Sun * used to add up the counts from each CPU to account for all the local
885d0ce359SJiebin Sun * counts. So percpu_counter_add_local() and percpu_counter_sub_local()
895d0ce359SJiebin Sun * should be used when a counter is updated frequently and read rarely.
905d0ce359SJiebin Sun */
915d0ce359SJiebin Sun static inline void
percpu_counter_add_local(struct percpu_counter * fbc,s64 amount)925d0ce359SJiebin Sun percpu_counter_add_local(struct percpu_counter *fbc, s64 amount)
935d0ce359SJiebin Sun {
945d0ce359SJiebin Sun percpu_counter_add_batch(fbc, amount, PERCPU_COUNTER_LOCAL_BATCH);
955d0ce359SJiebin Sun }
965d0ce359SJiebin Sun
percpu_counter_sum_positive(struct percpu_counter * fbc)97bf1d89c8SPeter Zijlstra static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc)
98bf1d89c8SPeter Zijlstra {
9902d21168SAndrew Morton s64 ret = __percpu_counter_sum(fbc);
100bf1d89c8SPeter Zijlstra return ret < 0 ? 0 : ret;
101bf1d89c8SPeter Zijlstra }
102bf1d89c8SPeter Zijlstra
percpu_counter_sum(struct percpu_counter * fbc)103bf1d89c8SPeter Zijlstra static inline s64 percpu_counter_sum(struct percpu_counter *fbc)
104bf1d89c8SPeter Zijlstra {
10502d21168SAndrew Morton return __percpu_counter_sum(fbc);
106bf1d89c8SPeter Zijlstra }
107bf1d89c8SPeter Zijlstra
percpu_counter_read(struct percpu_counter * fbc)1080216bfcfSMingming Cao static inline s64 percpu_counter_read(struct percpu_counter *fbc)
1091da177e4SLinus Torvalds {
1101da177e4SLinus Torvalds return fbc->count;
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds /*
1141da177e4SLinus Torvalds * It is possible for the percpu_counter_read() to return a small negative
1151da177e4SLinus Torvalds * number for some counter which should never be negative.
1160216bfcfSMingming Cao *
1171da177e4SLinus Torvalds */
percpu_counter_read_positive(struct percpu_counter * fbc)1180216bfcfSMingming Cao static inline s64 percpu_counter_read_positive(struct percpu_counter *fbc)
1191da177e4SLinus Torvalds {
1207e234520SQian Cai /* Prevent reloads of fbc->count */
1217e234520SQian Cai s64 ret = READ_ONCE(fbc->count);
1221da177e4SLinus Torvalds
1230216bfcfSMingming Cao if (ret >= 0)
1241da177e4SLinus Torvalds return ret;
125c84598bbSShaohua Li return 0;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds
percpu_counter_initialized(struct percpu_counter * fbc)12885dcbba3Sguoyayun static inline bool percpu_counter_initialized(struct percpu_counter *fbc)
1297f93cff9STheodore Ts'o {
1307f93cff9STheodore Ts'o return (fbc->counters != NULL);
1317f93cff9STheodore Ts'o }
1327f93cff9STheodore Ts'o
1337fa4cf92SJesper Dangaard Brouer #else /* !CONFIG_SMP */
1341da177e4SLinus Torvalds
1351da177e4SLinus Torvalds struct percpu_counter {
1360216bfcfSMingming Cao s64 count;
1371da177e4SLinus Torvalds };
1381da177e4SLinus Torvalds
percpu_counter_init_many(struct percpu_counter * fbc,s64 amount,gfp_t gfp,u32 nr_counters)139c439d5e8SMateusz Guzik static inline int percpu_counter_init_many(struct percpu_counter *fbc,
140c439d5e8SMateusz Guzik s64 amount, gfp_t gfp,
141c439d5e8SMateusz Guzik u32 nr_counters)
142c439d5e8SMateusz Guzik {
143c439d5e8SMateusz Guzik u32 i;
144c439d5e8SMateusz Guzik
145c439d5e8SMateusz Guzik for (i = 0; i < nr_counters; i++)
146c439d5e8SMateusz Guzik fbc[i].count = amount;
147c439d5e8SMateusz Guzik
148c439d5e8SMateusz Guzik return 0;
149c439d5e8SMateusz Guzik }
150c439d5e8SMateusz Guzik
percpu_counter_init(struct percpu_counter * fbc,s64 amount,gfp_t gfp)151908c7f19STejun Heo static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount,
152908c7f19STejun Heo gfp_t gfp)
1531da177e4SLinus Torvalds {
154c439d5e8SMateusz Guzik return percpu_counter_init_many(fbc, amount, gfp, 1);
155c439d5e8SMateusz Guzik }
156c439d5e8SMateusz Guzik
percpu_counter_destroy_many(struct percpu_counter * fbc,u32 nr_counters)157c439d5e8SMateusz Guzik static inline void percpu_counter_destroy_many(struct percpu_counter *fbc,
158c439d5e8SMateusz Guzik u32 nr_counters)
159c439d5e8SMateusz Guzik {
1601da177e4SLinus Torvalds }
1611da177e4SLinus Torvalds
percpu_counter_destroy(struct percpu_counter * fbc)1621da177e4SLinus Torvalds static inline void percpu_counter_destroy(struct percpu_counter *fbc)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds }
1651da177e4SLinus Torvalds
percpu_counter_set(struct percpu_counter * fbc,s64 amount)1663a587f47SPeter Zijlstra static inline void percpu_counter_set(struct percpu_counter *fbc, s64 amount)
1673a587f47SPeter Zijlstra {
1683a587f47SPeter Zijlstra fbc->count = amount;
1693a587f47SPeter Zijlstra }
1703a587f47SPeter Zijlstra
percpu_counter_compare(struct percpu_counter * fbc,s64 rhs)17127f5e0f6STim Chen static inline int percpu_counter_compare(struct percpu_counter *fbc, s64 rhs)
17227f5e0f6STim Chen {
17327f5e0f6STim Chen if (fbc->count > rhs)
17427f5e0f6STim Chen return 1;
17527f5e0f6STim Chen else if (fbc->count < rhs)
17627f5e0f6STim Chen return -1;
17727f5e0f6STim Chen else
17827f5e0f6STim Chen return 0;
17927f5e0f6STim Chen }
18027f5e0f6STim Chen
18180188b0dSDave Chinner static inline int
__percpu_counter_compare(struct percpu_counter * fbc,s64 rhs,s32 batch)18280188b0dSDave Chinner __percpu_counter_compare(struct percpu_counter *fbc, s64 rhs, s32 batch)
18380188b0dSDave Chinner {
18480188b0dSDave Chinner return percpu_counter_compare(fbc, rhs);
18580188b0dSDave Chinner }
18680188b0dSDave Chinner
1871da177e4SLinus Torvalds static inline void
percpu_counter_add(struct percpu_counter * fbc,s64 amount)18820e89767SPeter Zijlstra percpu_counter_add(struct percpu_counter *fbc, s64 amount)
1891da177e4SLinus Torvalds {
19088ad32a7SManfred Spraul unsigned long flags;
19188ad32a7SManfred Spraul
19288ad32a7SManfred Spraul local_irq_save(flags);
1931da177e4SLinus Torvalds fbc->count += amount;
19488ad32a7SManfred Spraul local_irq_restore(flags);
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds
197beb98686SHugh Dickins static inline bool
percpu_counter_limited_add(struct percpu_counter * fbc,s64 limit,s64 amount)198beb98686SHugh Dickins percpu_counter_limited_add(struct percpu_counter *fbc, s64 limit, s64 amount)
199beb98686SHugh Dickins {
200beb98686SHugh Dickins unsigned long flags;
201*1431996bSHugh Dickins bool good = false;
202beb98686SHugh Dickins s64 count;
203beb98686SHugh Dickins
204*1431996bSHugh Dickins if (amount == 0)
205*1431996bSHugh Dickins return true;
206*1431996bSHugh Dickins
207beb98686SHugh Dickins local_irq_save(flags);
208beb98686SHugh Dickins count = fbc->count + amount;
209*1431996bSHugh Dickins if ((amount > 0 && count <= limit) ||
210*1431996bSHugh Dickins (amount < 0 && count >= limit)) {
211beb98686SHugh Dickins fbc->count = count;
212*1431996bSHugh Dickins good = true;
213*1431996bSHugh Dickins }
214beb98686SHugh Dickins local_irq_restore(flags);
215*1431996bSHugh Dickins return good;
216beb98686SHugh Dickins }
217beb98686SHugh Dickins
2185d0ce359SJiebin Sun /* non-SMP percpu_counter_add_local is the same with percpu_counter_add */
2195d0ce359SJiebin Sun static inline void
percpu_counter_add_local(struct percpu_counter * fbc,s64 amount)2205d0ce359SJiebin Sun percpu_counter_add_local(struct percpu_counter *fbc, s64 amount)
2215d0ce359SJiebin Sun {
2225d0ce359SJiebin Sun percpu_counter_add(fbc, amount);
2235d0ce359SJiebin Sun }
2245d0ce359SJiebin Sun
2250c9cf2efSAnton Blanchard static inline void
percpu_counter_add_batch(struct percpu_counter * fbc,s64 amount,s32 batch)226104b4e51SNikolay Borisov percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
2270c9cf2efSAnton Blanchard {
2280c9cf2efSAnton Blanchard percpu_counter_add(fbc, amount);
2290c9cf2efSAnton Blanchard }
2300c9cf2efSAnton Blanchard
percpu_counter_read(struct percpu_counter * fbc)2310216bfcfSMingming Cao static inline s64 percpu_counter_read(struct percpu_counter *fbc)
2321da177e4SLinus Torvalds {
2331da177e4SLinus Torvalds return fbc->count;
2341da177e4SLinus Torvalds }
2351da177e4SLinus Torvalds
236c84598bbSShaohua Li /*
237c84598bbSShaohua Li * percpu_counter is intended to track positive numbers. In the UP case the
238c84598bbSShaohua Li * number should never be negative.
239c84598bbSShaohua Li */
percpu_counter_read_positive(struct percpu_counter * fbc)2400216bfcfSMingming Cao static inline s64 percpu_counter_read_positive(struct percpu_counter *fbc)
2411da177e4SLinus Torvalds {
2421da177e4SLinus Torvalds return fbc->count;
2431da177e4SLinus Torvalds }
2441da177e4SLinus Torvalds
percpu_counter_sum_positive(struct percpu_counter * fbc)24552d9f3b4SPeter Zijlstra static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc)
246e2bab3d9SAndrew Morton {
247e2bab3d9SAndrew Morton return percpu_counter_read_positive(fbc);
248e2bab3d9SAndrew Morton }
249e2bab3d9SAndrew Morton
percpu_counter_sum(struct percpu_counter * fbc)250bf1d89c8SPeter Zijlstra static inline s64 percpu_counter_sum(struct percpu_counter *fbc)
251bf1d89c8SPeter Zijlstra {
252bf1d89c8SPeter Zijlstra return percpu_counter_read(fbc);
253bf1d89c8SPeter Zijlstra }
254bf1d89c8SPeter Zijlstra
percpu_counter_initialized(struct percpu_counter * fbc)25585dcbba3Sguoyayun static inline bool percpu_counter_initialized(struct percpu_counter *fbc)
2567f93cff9STheodore Ts'o {
25785dcbba3Sguoyayun return true;
2587f93cff9STheodore Ts'o }
2597f93cff9STheodore Ts'o
percpu_counter_sync(struct percpu_counter * fbc)2600a4954a8SFeng Tang static inline void percpu_counter_sync(struct percpu_counter *fbc)
2610a4954a8SFeng Tang {
2620a4954a8SFeng Tang }
2631da177e4SLinus Torvalds #endif /* CONFIG_SMP */
2641da177e4SLinus Torvalds
percpu_counter_inc(struct percpu_counter * fbc)2651da177e4SLinus Torvalds static inline void percpu_counter_inc(struct percpu_counter *fbc)
2661da177e4SLinus Torvalds {
267aa0dff2dSPeter Zijlstra percpu_counter_add(fbc, 1);
2681da177e4SLinus Torvalds }
2691da177e4SLinus Torvalds
percpu_counter_dec(struct percpu_counter * fbc)2701da177e4SLinus Torvalds static inline void percpu_counter_dec(struct percpu_counter *fbc)
2711da177e4SLinus Torvalds {
272aa0dff2dSPeter Zijlstra percpu_counter_add(fbc, -1);
2731da177e4SLinus Torvalds }
2741da177e4SLinus Torvalds
percpu_counter_sub(struct percpu_counter * fbc,s64 amount)2753cb4f9faSPeter Zijlstra static inline void percpu_counter_sub(struct percpu_counter *fbc, s64 amount)
2763cb4f9faSPeter Zijlstra {
2773cb4f9faSPeter Zijlstra percpu_counter_add(fbc, -amount);
2783cb4f9faSPeter Zijlstra }
2793cb4f9faSPeter Zijlstra
2805d0ce359SJiebin Sun static inline void
percpu_counter_sub_local(struct percpu_counter * fbc,s64 amount)2815d0ce359SJiebin Sun percpu_counter_sub_local(struct percpu_counter *fbc, s64 amount)
2825d0ce359SJiebin Sun {
2835d0ce359SJiebin Sun percpu_counter_add_local(fbc, -amount);
2845d0ce359SJiebin Sun }
2855d0ce359SJiebin Sun
2861da177e4SLinus Torvalds #endif /* _LINUX_PERCPU_COUNTER_H */
287