XenevaOS
Loading...
Searching...
No Matches
refcount.h
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * DCL <linux/refcount.h> -- mainline's saturating refcount, over DCL's atomics.
4 *
5 * Why this exists at all: BaseHdr/drm/ is 101 byte-ports of mainline headers,
6 * and struct kref in mainline holds a refcount_t. drm_connector.c:1070 does
7 * not merely call a kref helper -- it reaches inside the struct and writes
8 *
9 * if (!refcount_dec_and_test(&conn->base.refcount.refcount))
10 * return;
11 *
12 * so the field has to be a refcount_t and this header has to provide the
13 * operations. DCL's own kref.h had the field as atomic_t, which is the older
14 * mainline layout; kref.h is rewritten below to match mainline's, and
15 * atomic_t stays as it is because tty/serial code uses it directly as a
16 * counter, not as a reference count.
17 *
18 * Saturation is the whole reason refcount_t is not just atomic_t, and it is
19 * kept here rather than simplified away. A wrapped reference count turns a
20 * live object into a freed one (or the reverse), and it does so silently,
21 * long after the mistake that caused it. Saturating trades that for a
22 * permanently stuck counter, which is at least loud in the debugger and
23 * cannot be mistaken for a working object.
24 *
25 * Two differences from mainline's implementation, neither semantic:
26 *
27 * - Ordering. Mainline picks relaxed/release/acquire per operation to save
28 * instructions. Every DCL atomic is __ATOMIC_SEQ_CST, which is stronger
29 * than all three, so the guarantees mainline documents hold here; the
30 * per-operation ordering simply is not spelled out because there is one
31 * ordering and it is the strong one.
32 *
33 * - The fetch-and-return forms. Mainline uses atomic_fetch_add_relaxed()
34 * and atomic_fetch_sub_release(), which return the value *before* the
35 * operation. DCL's atomic_add_return()/atomic_sub_return() return the
36 * value after, so the before-value is recovered by adding/subtracting i
37 * back. Same number, different arithmetic.
38 *
39 * refcount_dec_not_one() is not ported. Mainline declares it extern with the
40 * body in lib/refcount.c, and its only caller in this tree was
41 * drm_gem_shmem_helper.c, which is not vendored (see Vendored/README.md for
42 * why). It can be added with that caller.
43 */
44
45#ifndef __LINUX_REFCOUNT_H__
46#define __LINUX_REFCOUNT_H__
47
48#include <linux/atomic.h>
49#include <linux/compiler.h>
50/*
51 * Not a mainline include -- mainline reaches NULL through its own headers
52 * and never names the dependency. This port does name it, in the four inline
53 * wrappers that pass NULL for mainline's optional `int *oldp` out-parameter,
54 * and <linux/atomic.h> does not carry it here. <linux/stddef.h> is where DCL
55 * defines NULL; kernel.h reaches the same macro via <_null.h>, but that is
56 * kernel.h's business rather than a contract refcount.h can lean on.
57 */
58#include <linux/stddef.h> /* NULL */
60
61#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }
62#define REFCOUNT_MAX 0x7fffffff
63/*
64 * Mainline: INT_MIN / 2. Written as the literal because this tree builds
65 * freestanding and <limits.h> is not on the kernel include path; the value
66 * is what matters, and it is chosen to sit roughly equidistant from 0 and
67 * INT_MAX so that the "bad value" range it opens up is as small as an
68 * unchecked counter can make it.
69 */
70#define REFCOUNT_SATURATED ((int)0xc0000000)
71
79
80/*
81 * Defined in DCL/drm_refcount_shim.c. Mainline's version WARNs and then
82 * sets the counter to REFCOUNT_SATURATED; the set is the load-bearing half,
83 * since without it the counter keeps moving and the corruption the warning
84 * describes actually happens. The warning half has nowhere to go -- DCL has
85 * no WARN_ON that a developer would see from a driver fault -- so the
86 * saturation is preserved and the diagnostic is not pretended to.
87 */
89
95static inline void refcount_set(refcount_t *r, int n)
96{
97 atomic_set(&r->refs, n);
98}
99
106static inline unsigned int refcount_read(const refcount_t *r)
107{
108 return (unsigned int)atomic_read(&r->refs);
109}
110
111/*
112 * __refcount_add_not_zero() -- add @i unless the count is already zero.
113 *
114 * The zero test is inside the loop rather than before it because the count
115 * can reach zero between the read and the update; mainline expresses this as
116 * an atomic_try_cmpxchg() that refreshes its @old on failure, and the loop
117 * below is the same retry with DCL's atomic_cmpxchg(), which reports the
118 * observed value instead of updating the expected one in place.
119 *
120 * Returning the pre-add value is what makes the zero case distinguishable
121 * without a second read: old == 0 means "was already dead", and the update
122 * was not applied.
123 */
124static inline __must_check bool __refcount_add_not_zero(int i, refcount_t *r,
125 int *oldp)
126{
127 int old = atomic_read(&r->refs);
128
129 do {
130 if (!old)
131 break;
132 } while (atomic_cmpxchg(&r->refs, old, old + i) != old);
133
134 if (oldp)
135 *oldp = old;
136
137 if (old < 0 || old + i < 0)
139
140 return old;
141}
142
152static inline __must_check bool refcount_add_not_zero(int i, refcount_t *r)
153{
154 return __refcount_add_not_zero(i, r, NULL);
155}
156
165static inline __must_check bool refcount_inc_not_zero(refcount_t *r)
166{
167 return __refcount_add_not_zero(1, r, NULL);
168}
169
179static inline void __refcount_add(int i, refcount_t *r, int *oldp)
180{
181 int old = atomic_add_return(i, &r->refs) - i;
182
183 if (oldp)
184 *oldp = old;
185
186 if (!old)
188 else if (old < 0 || old + i < 0)
190}
191
199static inline void refcount_add(int i, refcount_t *r)
200{
201 __refcount_add(i, r, NULL);
202}
203
210static inline void refcount_inc(refcount_t *r)
211{
212 __refcount_add(1, r, NULL);
213}
214
224static inline __must_check bool __refcount_sub_and_test(int i, refcount_t *r,
225 int *oldp)
226{
227 int old = atomic_sub_return(i, &r->refs) + i;
228
229 if (oldp)
230 *oldp = old;
231
232 if (old > 0 && old == i)
233 return true;
234
235 if (old <= 0 || old - i < 0)
237
238 return false;
239}
240
250static inline __must_check bool refcount_sub_and_test(int i, refcount_t *r)
251{
252 return __refcount_sub_and_test(i, r, NULL);
253}
254
263static inline __must_check bool refcount_dec_and_test(refcount_t *r)
264{
265 return __refcount_sub_and_test(1, r, NULL);
266}
267
268/*
269 * refcount_dec() -- the plain decrement refcount_dec_and_test() above is
270 * built on, and mainline's neighbour to refcount_inc() (v7.2 refcount.h).
271 * Added with the virtio-gpu port: DCL/drm_shmem_shim.c's unpin drops
272 * pages_pin_count with it. Underflow saturates and warns exactly as the
273 * other arms do; the result __refcount_sub_and_test() also hands back is
274 * the part mainline's refcount_dec() drops.
275 */
276static inline void refcount_dec(refcount_t *r)
277{
278 __refcount_sub_and_test(1, r, NULL);
279}
280
281#endif /* __LINUX_REFCOUNT_H__ */
XETime t
Definition main.cpp:53
#define NULL
Definition actypes.h:561
#define __must_check
Definition compiler.h:114
void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t)
refcount_saturation_type
Definition refcount.h:72
@ REFCOUNT_DEC_LEAK
Definition refcount.h:77
@ REFCOUNT_ADD_UAF
Definition refcount.h:75
@ REFCOUNT_ADD_NOT_ZERO_OVF
Definition refcount.h:73
@ REFCOUNT_ADD_OVF
Definition refcount.h:74
@ REFCOUNT_SUB_UAF
Definition refcount.h:76
Definition refcount_types.h:27
atomic_t refs
Definition refcount_types.h:28