]> Git Repo - J-linux.git/blob - include/linux/file_ref.h
Merge tag 'riscv-for-linus-6.13-mw1' of git://git.kernel.org/pub/scm/linux/kernel...
[J-linux.git] / include / linux / file_ref.h
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 #ifndef _LINUX_FILE_REF_H
3 #define _LINUX_FILE_REF_H
4
5 #include <linux/atomic.h>
6 #include <linux/preempt.h>
7 #include <linux/types.h>
8
9 /*
10  * file_ref is a reference count implementation specifically for use by
11  * files. It takes inspiration from rcuref but differs in key aspects
12  * such as support for SLAB_TYPESAFE_BY_RCU type caches.
13  *
14  * FILE_REF_ONEREF                FILE_REF_MAXREF
15  * 0x0000000000000000UL      0x7FFFFFFFFFFFFFFFUL
16  * <-------------------valid ------------------->
17  *
18  *                       FILE_REF_SATURATED
19  * 0x8000000000000000UL 0xA000000000000000UL 0xBFFFFFFFFFFFFFFFUL
20  * <-----------------------saturation zone---------------------->
21  *
22  * FILE_REF_RELEASED                   FILE_REF_DEAD
23  * 0xC000000000000000UL         0xE000000000000000UL
24  * <-------------------dead zone------------------->
25  *
26  * FILE_REF_NOREF
27  * 0xFFFFFFFFFFFFFFFFUL
28  */
29
30 #ifdef CONFIG_64BIT
31 #define FILE_REF_ONEREF         0x0000000000000000UL
32 #define FILE_REF_MAXREF         0x7FFFFFFFFFFFFFFFUL
33 #define FILE_REF_SATURATED      0xA000000000000000UL
34 #define FILE_REF_RELEASED       0xC000000000000000UL
35 #define FILE_REF_DEAD           0xE000000000000000UL
36 #define FILE_REF_NOREF          0xFFFFFFFFFFFFFFFFUL
37 #else
38 #define FILE_REF_ONEREF         0x00000000U
39 #define FILE_REF_MAXREF         0x7FFFFFFFU
40 #define FILE_REF_SATURATED      0xA0000000U
41 #define FILE_REF_RELEASED       0xC0000000U
42 #define FILE_REF_DEAD           0xE0000000U
43 #define FILE_REF_NOREF          0xFFFFFFFFU
44 #endif
45
46 typedef struct {
47 #ifdef CONFIG_64BIT
48         atomic64_t refcnt;
49 #else
50         atomic_t refcnt;
51 #endif
52 } file_ref_t;
53
54 /**
55  * file_ref_init - Initialize a file reference count
56  * @ref: Pointer to the reference count
57  * @cnt: The initial reference count typically '1'
58  */
59 static inline void file_ref_init(file_ref_t *ref, unsigned long cnt)
60 {
61         atomic_long_set(&ref->refcnt, cnt - 1);
62 }
63
64 bool __file_ref_put(file_ref_t *ref, unsigned long cnt);
65
66 /**
67  * file_ref_get - Acquire one reference on a file
68  * @ref: Pointer to the reference count
69  *
70  * Similar to atomic_inc_not_zero() but saturates at FILE_REF_MAXREF.
71  *
72  * Provides full memory ordering.
73  *
74  * Return: False if the attempt to acquire a reference failed. This happens
75  *         when the last reference has been put already. True if a reference
76  *         was successfully acquired
77  */
78 static __always_inline __must_check bool file_ref_get(file_ref_t *ref)
79 {
80         /*
81          * Unconditionally increase the reference count with full
82          * ordering. The saturation and dead zones provide enough
83          * tolerance for this.
84          *
85          * If this indicates negative the file in question the fail can
86          * be freed and immediately reused due to SLAB_TYPSAFE_BY_RCU.
87          * Hence, unconditionally altering the file reference count to
88          * e.g., reset the file reference count back to the middle of
89          * the deadzone risk end up marking someone else's file as dead
90          * behind their back.
91          *
92          * It would be possible to do a careful:
93          *
94          * cnt = atomic_long_inc_return();
95          * if (likely(cnt >= 0))
96          *      return true;
97          *
98          * and then something like:
99          *
100          * if (cnt >= FILE_REF_RELEASE)
101          *      atomic_long_try_cmpxchg(&ref->refcnt, &cnt, FILE_REF_DEAD),
102          *
103          * to set the value back to the middle of the deadzone. But it's
104          * practically impossible to go from FILE_REF_DEAD to
105          * FILE_REF_ONEREF. It would need 2305843009213693952/2^61
106          * file_ref_get()s to resurrect such a dead file.
107          */
108         return !atomic_long_add_negative(1, &ref->refcnt);
109 }
110
111 /**
112  * file_ref_inc - Acquire one reference on a file
113  * @ref: Pointer to the reference count
114  *
115  * Acquire an additional reference on a file. Warns if the caller didn't
116  * already hold a reference.
117  */
118 static __always_inline void file_ref_inc(file_ref_t *ref)
119 {
120         long prior = atomic_long_fetch_inc_relaxed(&ref->refcnt);
121         WARN_ONCE(prior < 0, "file_ref_inc() on a released file reference");
122 }
123
124 /**
125  * file_ref_put -- Release a file reference
126  * @ref:        Pointer to the reference count
127  *
128  * Provides release memory ordering, such that prior loads and stores
129  * are done before, and provides an acquire ordering on success such
130  * that free() must come after.
131  *
132  * Return: True if this was the last reference with no future references
133  *         possible. This signals the caller that it can safely release
134  *         the object which is protected by the reference counter.
135  *         False if there are still active references or the put() raced
136  *         with a concurrent get()/put() pair. Caller is not allowed to
137  *         release the protected object.
138  */
139 static __always_inline __must_check bool file_ref_put(file_ref_t *ref)
140 {
141         long cnt;
142
143         /*
144          * While files are SLAB_TYPESAFE_BY_RCU and thus file_ref_put()
145          * calls don't risk UAFs when a file is recyclyed, it is still
146          * vulnerable to UAFs caused by freeing the whole slab page once
147          * it becomes unused. Prevent file_ref_put() from being
148          * preempted protects against this.
149          */
150         guard(preempt)();
151         /*
152          * Unconditionally decrease the reference count. The saturation
153          * and dead zones provide enough tolerance for this. If this
154          * fails then we need to handle the last reference drop and
155          * cases inside the saturation and dead zones.
156          */
157         cnt = atomic_long_dec_return(&ref->refcnt);
158         if (cnt >= 0)
159                 return false;
160         return __file_ref_put(ref, cnt);
161 }
162
163 /**
164  * file_ref_read - Read the number of file references
165  * @ref: Pointer to the reference count
166  *
167  * Return: The number of held references (0 ... N)
168  */
169 static inline unsigned long file_ref_read(file_ref_t *ref)
170 {
171         unsigned long c = atomic_long_read(&ref->refcnt);
172
173         /* Return 0 if within the DEAD zone. */
174         return c >= FILE_REF_RELEASED ? 0 : c + 1;
175 }
176
177 #endif
This page took 0.037089 seconds and 4 git commands to generate.