File Index
Files and directories
Sign in
FILE

file_ref.h

File preview with syntax highlighting.

linux-6.19.14/include/linux/file_ref.h C
  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
 46typedef 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 */
 59static inline void file_ref_init(file_ref_t *ref, unsigned long cnt)
 60{
 61	atomic_long_set(&ref->refcnt, cnt - 1);
 62}
 63
 64bool __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 */
 78static __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 */
118static __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 */
139static __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_put_close - drop a reference expecting it would transition to FILE_REF_NOREF
165 * @ref:	Pointer to the reference count
166 *
167 * Semantically it is equivalent to calling file_ref_put(), but it trades lower
168 * performance in face of other CPUs also modifying the refcount for higher
169 * performance when this happens to be the last reference.
170 *
171 * For the last reference file_ref_put() issues 2 atomics. One to drop the
172 * reference and another to transition it to FILE_REF_DEAD. This routine does
173 * the work in one step, but in order to do it has to pre-read the variable which
174 * decreases scalability.
175 *
176 * Use with close() et al, stick to file_ref_put() by default.
177 */
178static __always_inline __must_check bool file_ref_put_close(file_ref_t *ref)
179{
180	long old;
181
182	old = atomic_long_read(&ref->refcnt);
183	if (likely(old == FILE_REF_ONEREF)) {
184		if (likely(atomic_long_try_cmpxchg(&ref->refcnt, &old, FILE_REF_DEAD)))
185			return true;
186	}
187	return file_ref_put(ref);
188}
189
190/**
191 * file_ref_read - Read the number of file references
192 * @ref: Pointer to the reference count
193 *
194 * Return: The number of held references (0 ... N)
195 */
196static inline unsigned long file_ref_read(file_ref_t *ref)
197{
198	unsigned long c = atomic_long_read(&ref->refcnt);
199
200	/* Return 0 if within the DEAD zone. */
201	return c >= FILE_REF_RELEASED ? 0 : c + 1;
202}
203
204/*
205 * __file_ref_read_raw - Return the value stored in ref->refcnt
206 * @ref: Pointer to the reference count
207 *
208 * Return: The raw value found in the counter
209 *
210 * A hack for file_needs_f_pos_lock(), you probably want to use
211 * file_ref_read() instead.
212 */
213static inline unsigned long __file_ref_read_raw(file_ref_t *ref)
214{
215	return atomic_long_read(&ref->refcnt);
216}
217
218#endif
~/ Read-only file preview.