/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _LINUX_FILE_REF_H
#define _LINUX_FILE_REF_H
#include <linux/atomic.h>
#include <linux/preempt.h>
#include <linux/types.h>
/*
* file_ref is a reference count implementation specifically for use by
* files . It takes inspiration from rcuref but differs in key aspects
* such as support for SLAB_TYPESAFE_BY_RCU type caches .
*
* FILE_REF_ONEREF FILE_REF_MAXREF
* 0 x0000000000000000UL 0 x7FFFFFFFFFFFFFFFUL
* < - - - - - - - - - - - - - - - - - - - valid - - - - - - - - - - - - - - - - - - - >
*
* FILE_REF_SATURATED
* 0 x8000000000000000UL 0 xA000000000000000UL 0 xBFFFFFFFFFFFFFFFUL
* < - - - - - - - - - - - - - - - - - - - - - - - saturation zone - - - - - - - - - - - - - - - - - - - - - - >
*
* FILE_REF_RELEASED FILE_REF_DEAD
* 0 xC000000000000000UL 0 xE000000000000000UL
* < - - - - - - - - - - - - - - - - - - - dead zone - - - - - - - - - - - - - - - - - - - >
*
* FILE_REF_NOREF
* 0 xFFFFFFFFFFFFFFFFUL
*/
#ifdef CONFIG_64BIT
#define FILE_REF_ONEREF 0 x0000000000000000UL
#define FILE_REF_MAXREF 0 x7FFFFFFFFFFFFFFFUL
#define FILE_REF_SATURATED 0 xA000000000000000UL
#define FILE_REF_RELEASED 0 xC000000000000000UL
#define FILE_REF_DEAD 0 xE000000000000000UL
#define FILE_REF_NOREF 0 xFFFFFFFFFFFFFFFFUL
#else
#define FILE_REF_ONEREF 0 x00000000U
#define FILE_REF_MAXREF 0 x7FFFFFFFU
#define FILE_REF_SATURATED 0 xA0000000U
#define FILE_REF_RELEASED 0 xC0000000U
#define FILE_REF_DEAD 0 xE0000000U
#define FILE_REF_NOREF 0 xFFFFFFFFU
#endif
typedef struct {
#ifdef CONFIG_64BIT
atomic64_t refcnt;
#else
atomic_t refcnt;
#endif
} file_ref_t;
/**
* file_ref_init - Initialize a file reference count
* @ ref : Pointer to the reference count
* @ cnt : The initial reference count typically ' 1 '
*/
static inline void file_ref_init(file_ref_t *ref, unsigned long cnt)
{
atomic_long_set(&ref->refcnt, cnt - 1 );
}
bool __file_ref_put(file_ref_t *ref, unsigned long cnt);
/**
* file_ref_get - Acquire one reference on a file
* @ ref : Pointer to the reference count
*
* Similar to atomic_inc_not_zero ( ) but saturates at FILE_REF_MAXREF .
*
* Provides full memory ordering .
*
* Return : False if the attempt to acquire a reference failed . This happens
* when the last reference has been put already . True if a reference
* was successfully acquired
*/
static __always_inline __must_check bool file_ref_get(file_ref_t *ref)
{
/*
* Unconditionally increase the reference count with full
* ordering . The saturation and dead zones provide enough
* tolerance for this .
*
* If this indicates negative the file in question the fail can
* be freed and immediately reused due to SLAB_TYPSAFE_BY_RCU .
* Hence , unconditionally altering the file reference count to
* e . g . , reset the file reference count back to the middle of
* the deadzone risk end up marking someone else ' s file as dead
* behind their back .
*
* It would be possible to do a careful :
*
* cnt = atomic_long_inc_return ( ) ;
* if ( likely ( cnt > = 0 ) )
* return true ;
*
* and then something like :
*
* if ( cnt > = FILE_REF_RELEASE )
* atomic_long_try_cmpxchg ( & ref - > refcnt , & cnt , FILE_REF_DEAD ) ,
*
* to set the value back to the middle of the deadzone . But it ' s
* practically impossible to go from FILE_REF_DEAD to
* FILE_REF_ONEREF . It would need 2305843009213693952 / 2 ^ 61
* file_ref_get ( ) s to resurrect such a dead file .
*/
return !atomic_long_add_negative(1 , &ref->refcnt);
}
/**
* file_ref_inc - Acquire one reference on a file
* @ ref : Pointer to the reference count
*
* Acquire an additional reference on a file . Warns if the caller didn ' t
* already hold a reference .
*/
static __always_inline void file_ref_inc(file_ref_t *ref)
{
long prior = atomic_long_fetch_inc_relaxed(&ref->refcnt);
WARN_ONCE(prior < 0 , "file_ref_inc() on a released file reference" );
}
/**
* file_ref_put - - Release a file reference
* @ ref : Pointer to the reference count
*
* Provides release memory ordering , such that prior loads and stores
* are done before , and provides an acquire ordering on success such
* that free ( ) must come after .
*
* Return : True if this was the last reference with no future references
* possible . This signals the caller that it can safely release
* the object which is protected by the reference counter .
* False if there are still active references or the put ( ) raced
* with a concurrent get ( ) / put ( ) pair . Caller is not allowed to
* release the protected object .
*/
static __always_inline __must_check bool file_ref_put(file_ref_t *ref)
{
long cnt;
/*
* While files are SLAB_TYPESAFE_BY_RCU and thus file_ref_put ( )
* calls don ' t risk UAFs when a file is recyclyed , it is still
* vulnerable to UAFs caused by freeing the whole slab page once
* it becomes unused . Prevent file_ref_put ( ) from being
* preempted protects against this .
*/
guard(preempt)();
/*
* Unconditionally decrease the reference count . The saturation
* and dead zones provide enough tolerance for this . If this
* fails then we need to handle the last reference drop and
* cases inside the saturation and dead zones .
*/
cnt = atomic_long_dec_return(&ref->refcnt);
if (cnt >= 0 )
return false ;
return __file_ref_put(ref, cnt);
}
/**
* file_ref_put_close - drop a reference expecting it would transition to FILE_REF_NOREF
* @ ref : Pointer to the reference count
*
* Semantically it is equivalent to calling file_ref_put ( ) , but it trades lower
* performance in face of other CPUs also modifying the refcount for higher
* performance when this happens to be the last reference .
*
* For the last reference file_ref_put ( ) issues 2 atomics . One to drop the
* reference and another to transition it to FILE_REF_DEAD . This routine does
* the work in one step , but in order to do it has to pre - read the variable which
* decreases scalability .
*
* Use with close ( ) et al , stick to file_ref_put ( ) by default .
*/
static __always_inline __must_check bool file_ref_put_close(file_ref_t *ref)
{
long old;
old = atomic_long_read(&ref->refcnt);
if (likely(old == FILE_REF_ONEREF)) {
if (likely(atomic_long_try_cmpxchg(&ref->refcnt, &old, FILE_REF_DEAD)))
return true ;
}
return file_ref_put(ref);
}
/**
* file_ref_read - Read the number of file references
* @ ref : Pointer to the reference count
*
* Return : The number of held references ( 0 . . . N )
*/
static inline unsigned long file_ref_read(file_ref_t *ref)
{
unsigned long c = atomic_long_read(&ref->refcnt);
/* Return 0 if within the DEAD zone. */
return c >= FILE_REF_RELEASED ? 0 : c + 1 ;
}
/*
* _ _ file_ref_read_raw - Return the value stored in ref - > refcnt
* @ ref : Pointer to the reference count
*
* Return : The raw value found in the counter
*
* A hack for file_needs_f_pos_lock ( ) , you probably want to use
* file_ref_read ( ) instead .
*/
static inline unsigned long __file_ref_read_raw(file_ref_t *ref)
{
return atomic_long_read(&ref->refcnt);
}
#endif
Messung V0.5 in Prozent C=95 H=100 G=97
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland