1#ifndef _LINUX_PERCPU_COUNTER_H
2#define _LINUX_PERCPU_COUNTER_H
3/*
4 * A simple "approximate counter" for use in ext2 and ext3 superblocks.
5 *
6 * WARNING: these things are HUGE.  4 kbytes per counter on 32-way P4.
7 */
8
9#include <linux/spinlock.h>
10#include <linux/smp.h>
11#include <linux/list.h>
12#include <linux/threads.h>
13#include <linux/percpu.h>
14#include <linux/types.h>
15#include <linux/gfp.h>
16
17#ifdef CONFIG_SMP
18
19struct percpu_counter {
20	raw_spinlock_t lock;
21	s64 count;
22#ifdef CONFIG_HOTPLUG_CPU
23	struct list_head list;	/* All percpu_counters are on a list */
24#endif
25	s32 __percpu *counters;
26};
27
28extern int percpu_counter_batch;
29
30int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp,
31			  struct lock_class_key *key);
32
33#define percpu_counter_init(fbc, value, gfp)				\
34	({								\
35		static struct lock_class_key __key;			\
36									\
37		__percpu_counter_init(fbc, value, gfp, &__key);		\
38	})
39
40void percpu_counter_destroy(struct percpu_counter *fbc);
41void percpu_counter_set(struct percpu_counter *fbc, s64 amount);
42void __percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch);
43s64 __percpu_counter_sum(struct percpu_counter *fbc);
44int __percpu_counter_compare(struct percpu_counter *fbc, s64 rhs, s32 batch);
45
46static inline int percpu_counter_compare(struct percpu_counter *fbc, s64 rhs)
47{
48	return __percpu_counter_compare(fbc, rhs, percpu_counter_batch);
49}
50
51static inline void percpu_counter_add(struct percpu_counter *fbc, s64 amount)
52{
53	__percpu_counter_add(fbc, amount, percpu_counter_batch);
54}
55
56static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc)
57{
58	s64 ret = __percpu_counter_sum(fbc);
59	return ret < 0 ? 0 : ret;
60}
61
62static inline s64 percpu_counter_sum(struct percpu_counter *fbc)
63{
64	return __percpu_counter_sum(fbc);
65}
66
67static inline s64 percpu_counter_read(struct percpu_counter *fbc)
68{
69	return fbc->count;
70}
71
72/*
73 * It is possible for the percpu_counter_read() to return a small negative
74 * number for some counter which should never be negative.
75 *
76 */
77static inline s64 percpu_counter_read_positive(struct percpu_counter *fbc)
78{
79	s64 ret = fbc->count;
80
81	barrier();		/* Prevent reloads of fbc->count */
82	if (ret >= 0)
83		return ret;
84	return 0;
85}
86
87static inline int percpu_counter_initialized(struct percpu_counter *fbc)
88{
89	return (fbc->counters != NULL);
90}
91
92#else /* !CONFIG_SMP */
93
94struct percpu_counter {
95	s64 count;
96};
97
98static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount,
99				      gfp_t gfp)
100{
101	fbc->count = amount;
102	return 0;
103}
104
105static inline void percpu_counter_destroy(struct percpu_counter *fbc)
106{
107}
108
109static inline void percpu_counter_set(struct percpu_counter *fbc, s64 amount)
110{
111	fbc->count = amount;
112}
113
114static inline int percpu_counter_compare(struct percpu_counter *fbc, s64 rhs)
115{
116	if (fbc->count > rhs)
117		return 1;
118	else if (fbc->count < rhs)
119		return -1;
120	else
121		return 0;
122}
123
124static inline int
125__percpu_counter_compare(struct percpu_counter *fbc, s64 rhs, s32 batch)
126{
127	return percpu_counter_compare(fbc, rhs);
128}
129
130static inline void
131percpu_counter_add(struct percpu_counter *fbc, s64 amount)
132{
133	preempt_disable();
134	fbc->count += amount;
135	preempt_enable();
136}
137
138static inline void
139__percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch)
140{
141	percpu_counter_add(fbc, amount);
142}
143
144static inline s64 percpu_counter_read(struct percpu_counter *fbc)
145{
146	return fbc->count;
147}
148
149/*
150 * percpu_counter is intended to track positive numbers. In the UP case the
151 * number should never be negative.
152 */
153static inline s64 percpu_counter_read_positive(struct percpu_counter *fbc)
154{
155	return fbc->count;
156}
157
158static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc)
159{
160	return percpu_counter_read_positive(fbc);
161}
162
163static inline s64 percpu_counter_sum(struct percpu_counter *fbc)
164{
165	return percpu_counter_read(fbc);
166}
167
168static inline int percpu_counter_initialized(struct percpu_counter *fbc)
169{
170	return 1;
171}
172
173#endif	/* CONFIG_SMP */
174
175static inline void percpu_counter_inc(struct percpu_counter *fbc)
176{
177	percpu_counter_add(fbc, 1);
178}
179
180static inline void percpu_counter_dec(struct percpu_counter *fbc)
181{
182	percpu_counter_add(fbc, -1);
183}
184
185static inline void percpu_counter_sub(struct percpu_counter *fbc, s64 amount)
186{
187	percpu_counter_add(fbc, -amount);
188}
189
190#endif /* _LINUX_PERCPU_COUNTER_H */
191