Skip to content

Commit 816eabd

Browse files
nbdd0121jannau
authored andcommitted
rust: implement kernel::sync::Refcount
This is a wrapping layer of `include/linux/refcount.h`. Currently the kernel refcount has already been used in `Arc`, however it calls into FFI directly. [boqun: Add the missing <> for the link in comment] Signed-off-by: Gary Guo <gary@garyguo.net> Signed-off-by: Boqun Feng <boqun.feng@gmail.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Boqun Feng <boqun.feng@gmail.com> Reviewed-by: Fiona Behrens <me@kloenk.dev> Reviewed-by: Benno Lossin <lossin@kernel.org> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev> Link: https://lore.kernel.org/r/20250723233312.3304339-2-gary@kernel.org
1 parent 45ae3c4 commit 816eabd

3 files changed

Lines changed: 110 additions & 0 deletions

File tree

rust/helpers/refcount.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,21 @@ refcount_t rust_helper_REFCOUNT_INIT(int n)
77
return (refcount_t)REFCOUNT_INIT(n);
88
}
99

10+
void rust_helper_refcount_set(refcount_t *r, int n)
11+
{
12+
refcount_set(r, n);
13+
}
14+
1015
void rust_helper_refcount_inc(refcount_t *r)
1116
{
1217
refcount_inc(r);
1318
}
1419

20+
void rust_helper_refcount_dec(refcount_t *r)
21+
{
22+
refcount_dec(r);
23+
}
24+
1525
bool rust_helper_refcount_dec_and_test(refcount_t *r)
1626
{
1727
return refcount_dec_and_test(r);

rust/kernel/sync.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod lock;
1919
mod locked_by;
2020
pub mod poll;
2121
pub mod rcu;
22+
mod refcount;
2223

2324
pub use arc::{Arc, ArcBorrow, UniqueArc};
2425
pub use completion::Completion;
@@ -27,6 +28,7 @@ pub use lock::global::{global_lock, GlobalGuard, GlobalLock, GlobalLockBackend,
2728
pub use lock::mutex::{new_mutex, Mutex, MutexGuard};
2829
pub use lock::spinlock::{new_spinlock, SpinLock, SpinLockGuard};
2930
pub use locked_by::LockedBy;
31+
pub use refcount::Refcount;
3032

3133
/// Represents a lockdep class. It's a wrapper around C's `lock_class_key`.
3234
#[repr(transparent)]

rust/kernel/sync/refcount.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Atomic reference counting.
4+
//!
5+
//! C header: [`include/linux/refcount.h`](srctree/include/linux/refcount.h)
6+
7+
use crate::build_assert;
8+
use crate::types::Opaque;
9+
10+
/// Atomic reference counter.
11+
///
12+
/// This type is conceptually an atomic integer, but provides saturation semantics compared to
13+
/// normal atomic integers. Values in the negative range when viewed as a signed integer are
14+
/// saturation (bad) values. For details about the saturation semantics, please refer to top of
15+
/// [`include/linux/refcount.h`](srctree/include/linux/refcount.h).
16+
///
17+
/// Wraps the kernel's C `refcount_t`.
18+
#[repr(transparent)]
19+
pub struct Refcount(Opaque<bindings::refcount_t>);
20+
21+
impl Refcount {
22+
/// Construct a new [`Refcount`] from an initial value.
23+
///
24+
/// The initial value should be non-saturated.
25+
#[inline]
26+
pub fn new(value: i32) -> Self {
27+
build_assert!(value >= 0, "initial value saturated");
28+
// SAFETY: There are no safety requirements for this FFI call.
29+
Self(Opaque::new(unsafe { bindings::REFCOUNT_INIT(value) }))
30+
}
31+
32+
#[inline]
33+
fn as_ptr(&self) -> *mut bindings::refcount_t {
34+
self.0.get()
35+
}
36+
37+
/// Set a refcount's value.
38+
#[inline]
39+
pub fn set(&self, value: i32) {
40+
// SAFETY: `self.as_ptr()` is valid.
41+
unsafe { bindings::refcount_set(self.as_ptr(), value) }
42+
}
43+
44+
/// Increment a refcount.
45+
///
46+
/// It will saturate if overflows and `WARN`. It will also `WARN` if the refcount is 0, as this
47+
/// represents a possible use-after-free condition.
48+
///
49+
/// Provides no memory ordering, it is assumed that caller already has a reference on the
50+
/// object.
51+
#[inline]
52+
pub fn inc(&self) {
53+
// SAFETY: self is valid.
54+
unsafe { bindings::refcount_inc(self.as_ptr()) }
55+
}
56+
57+
/// Decrement a refcount.
58+
///
59+
/// It will `WARN` on underflow and fail to decrement when saturated.
60+
///
61+
/// Provides release memory ordering, such that prior loads and stores are done
62+
/// before.
63+
#[inline]
64+
pub fn dec(&self) {
65+
// SAFETY: `self.as_ptr()` is valid.
66+
unsafe { bindings::refcount_dec(self.as_ptr()) }
67+
}
68+
69+
/// Decrement a refcount and test if it is 0.
70+
///
71+
/// It will `WARN` on underflow and fail to decrement when saturated.
72+
///
73+
/// Provides release memory ordering, such that prior loads and stores are done
74+
/// before, and provides an acquire ordering on success such that memory deallocation
75+
/// must come after.
76+
///
77+
/// Returns true if the resulting refcount is 0, false otherwise.
78+
///
79+
/// # Notes
80+
///
81+
/// A common pattern of using `Refcount` is to free memory when the reference count reaches
82+
/// zero. This means that the reference to `Refcount` could become invalid after calling this
83+
/// function. This is fine as long as the reference to `Refcount` is no longer used when this
84+
/// function returns `false`. It is not necessary to use raw pointers in this scenario, see
85+
/// <https://github.com/rust-lang/rust/issues/55005>.
86+
#[inline]
87+
#[must_use = "use `dec` instead if you do not need to test if it is 0"]
88+
pub fn dec_and_test(&self) -> bool {
89+
// SAFETY: `self.as_ptr()` is valid.
90+
unsafe { bindings::refcount_dec_and_test(self.as_ptr()) }
91+
}
92+
}
93+
94+
// SAFETY: `refcount_t` is thread-safe.
95+
unsafe impl Send for Refcount {}
96+
97+
// SAFETY: `refcount_t` is thread-safe.
98+
unsafe impl Sync for Refcount {}

0 commit comments

Comments
 (0)