Skip to content

Commit 6a4673a

Browse files
committed
rust: update Ref to use the kernel's refcount_t.
Saturate the refcount instead of panicking. It also leverages existing C code instead of reimplementing functionality. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent d8c6b9c commit 6a4673a

File tree

3 files changed

+89
-69
lines changed

3 files changed

+89
-69
lines changed

drivers/android/process.rs

+18-16
Original file line numberDiff line numberDiff line change
@@ -293,22 +293,24 @@ unsafe impl Sync for Process {}
293293

294294
impl Process {
295295
fn new(ctx: Arc<Context>) -> Result<Ref<Self>> {
296-
let mut proc_ref = Ref::try_new(Self {
297-
ref_count: RefCount::new(),
298-
ctx,
299-
// SAFETY: `inner` is initialised in the call to `mutex_init` below.
300-
inner: unsafe { Mutex::new(ProcessInner::new()) },
301-
// SAFETY: `node_refs` is initialised in the call to `mutex_init` below.
302-
node_refs: unsafe { Mutex::new(ProcessNodeRefs::new()) },
303-
})?;
304-
let process = Ref::get_mut(&mut proc_ref).ok_or(Error::EINVAL)?;
305-
// SAFETY: `inner` is pinned behind the `Arc` reference.
306-
let pinned = unsafe { Pin::new_unchecked(&process.inner) };
307-
kernel::mutex_init!(pinned, "Process::inner");
308-
// SAFETY: `node_refs` is pinned behind the `Arc` reference.
309-
let pinned = unsafe { Pin::new_unchecked(&process.node_refs) };
310-
kernel::mutex_init!(pinned, "Process::node_refs");
311-
Ok(proc_ref)
296+
Ref::try_new_and_init(
297+
Self {
298+
ref_count: RefCount::default(),
299+
ctx,
300+
// SAFETY: `inner` is initialised in the call to `mutex_init` below.
301+
inner: unsafe { Mutex::new(ProcessInner::new()) },
302+
// SAFETY: `node_refs` is initialised in the call to `mutex_init` below.
303+
node_refs: unsafe { Mutex::new(ProcessNodeRefs::new()) },
304+
},
305+
|process| {
306+
// SAFETY: `inner` is pinned behind the `Ref` reference.
307+
let pinned = unsafe { Pin::new_unchecked(&process.inner) };
308+
kernel::mutex_init!(pinned, "Process::inner");
309+
// SAFETY: `node_refs` is pinned behind the `Ref` reference.
310+
let pinned = unsafe { Pin::new_unchecked(&process.node_refs) };
311+
kernel::mutex_init!(pinned, "Process::node_refs");
312+
},
313+
)
312314
}
313315

314316
/// Attemps to fetch a work item from the process queue.

rust/helpers.c

+18
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,24 @@ void rust_helper_mutex_lock(struct mutex *lock)
130130
}
131131
EXPORT_SYMBOL_GPL(rust_helper_mutex_lock);
132132

133+
void rust_helper_refcount_set(refcount_t *r, unsigned int n)
134+
{
135+
refcount_set(r, n);
136+
}
137+
EXPORT_SYMBOL_GPL(rust_helper_refcount_set);
138+
139+
void rust_helper_refcount_inc(refcount_t *r)
140+
{
141+
refcount_inc(r);
142+
}
143+
EXPORT_SYMBOL_GPL(rust_helper_refcount_inc);
144+
145+
bool rust_helper_refcount_dec_and_test(refcount_t *r)
146+
{
147+
return refcount_dec_and_test(r);
148+
}
149+
EXPORT_SYMBOL_GPL(rust_helper_refcount_dec_and_test);
150+
133151
/* We use bindgen's --size_t-is-usize option to bind the C size_t type
134152
* as the Rust usize type, so we can use it in contexts where Rust
135153
* expects a usize like slice (array) indices. usize is defined to be

rust/kernel/sync/arc.rs

+53-53
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,24 @@
77
//! underlying object when it reaches zero. It is also safe to use concurrently from multiple
88
//! threads.
99
//!
10-
//! It is different from the standard library's [`Arc`] in two ways: it does not support weak
11-
//! references, which allows it to be smaller -- a single pointer-sized integer; it allows users to
12-
//! safely increment the reference count from a single reference to the underlying object.
10+
//! It is different from the standard library's [`Arc`] in at least three ways:
11+
//! 1. It does not support weak references, which allows it to be smaller: a single pointer-sized
12+
//! integer;
13+
//! 2. It allows users to safely increment the reference count from a single reference to the
14+
//! underlying object. That is, go from `&T` to [`Ref<T>`];
15+
//! 3. It saturates the reference count instead of aborting when it goes over a threshold.
1316
//!
1417
//! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
1518
16-
use crate::Result;
19+
use crate::{bindings, Result};
1720
use alloc::boxed::Box;
18-
use core::{
19-
mem::ManuallyDrop,
20-
ops::Deref,
21-
ptr::NonNull,
22-
sync::atomic::{fence, AtomicUsize, Ordering},
23-
};
21+
use core::{cell::UnsafeCell, convert::AsRef, mem::ManuallyDrop, ops::Deref, ptr::NonNull};
22+
23+
extern "C" {
24+
fn rust_helper_refcount_set(r: *mut bindings::refcount_t, n: u32);
25+
fn rust_helper_refcount_inc(r: *mut bindings::refcount_t);
26+
fn rust_helper_refcount_dec_and_test(r: *mut bindings::refcount_t) -> bool;
27+
}
2428

2529
/// A reference-counted pointer to an instance of `T`.
2630
///
@@ -30,7 +34,8 @@ use core::{
3034
/// # Invariants
3135
///
3236
/// The value stored in [`RefCounted::get_count`] corresponds to the number of instances of [`Ref`]
33-
/// that point to that instance of `T`.
37+
/// that point to that instance of `T`. So it is always non-zero (because at least one [`Ref`]
38+
/// exists).
3439
pub struct Ref<T: RefCounted + ?Sized> {
3540
ptr: NonNull<T>,
3641
}
@@ -49,8 +54,19 @@ unsafe impl<T: RefCounted + ?Sized + Sync + Send> Sync for Ref<T> {}
4954
impl<T: RefCounted> Ref<T> {
5055
/// Constructs a new reference counted instance of `T`.
5156
pub fn try_new(contents: T) -> Result<Self> {
52-
let boxed = Box::try_new(contents)?;
53-
boxed.get_count().count.store(1, Ordering::Relaxed);
57+
Self::try_new_and_init(contents, |_| {})
58+
}
59+
60+
/// Constructs a new reference counted instance of `T` and calls the initialisation function.
61+
///
62+
/// This is useful because it provides a mutable reference to `T` at its final location.
63+
pub fn try_new_and_init<U: FnOnce(&mut T)>(contents: T, init: U) -> Result<Self> {
64+
let mut boxed = Box::try_new(contents)?;
65+
// INVARIANT: The refcount is initialised to a non-zero value.
66+
// SAFETY: We just allocated the object, no other references exist to it, so it is safe to
67+
// initialise the refcount to 1.
68+
unsafe { rust_helper_refcount_set(boxed.get_count().count.get(), 1) };
69+
init(&mut boxed);
5470
let ptr = NonNull::from(Box::leak(boxed));
5571
Ok(Ref { ptr })
5672
}
@@ -62,28 +78,15 @@ impl<T: RefCounted + ?Sized> Ref<T> {
6278
/// It works by incrementing the current reference count as part of constructing the new
6379
/// pointer.
6480
pub fn new_from(obj: &T) -> Self {
65-
let ref_count = obj.get_count();
66-
let cur = ref_count.count.fetch_add(1, Ordering::Relaxed);
67-
if cur == usize::MAX {
68-
panic!("Reference count overflowed");
69-
}
81+
// INVARIANT: C `refcount_inc` saturates the refcount, so it cannot overflow to zero.
82+
// SAFETY: By the type invariant, there is necessarily a reference to the object, so it is
83+
// safe to increment the refcount.
84+
unsafe { rust_helper_refcount_inc(obj.get_count().count.get()) };
7085
Self {
7186
ptr: NonNull::from(obj),
7287
}
7388
}
7489

75-
/// Returns a mutable reference to `T` iff the reference count is one. Otherwise returns
76-
/// [`None`].
77-
pub fn get_mut(&mut self) -> Option<&mut T> {
78-
// Synchronises with the decrement in `drop`.
79-
if self.get_count().count.load(Ordering::Acquire) != 1 {
80-
return None;
81-
}
82-
// SAFETY: Since there is only one reference, we know it isn't possible for another thread
83-
// to concurrently call this.
84-
Some(unsafe { self.ptr.as_mut() })
85-
}
86-
8790
/// Determines if two reference-counted pointers point to the same underlying instance of `T`.
8891
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
8992
core::ptr::eq(a.ptr.as_ptr(), b.ptr.as_ptr())
@@ -126,22 +129,31 @@ impl<T: RefCounted + ?Sized> Clone for Ref<T> {
126129
}
127130
}
128131

132+
impl<T: RefCounted + ?Sized> AsRef<T> for Ref<T> {
133+
fn as_ref(&self) -> &T {
134+
// SAFETY: By the type invariant, there is necessarily a reference to the object, so it is
135+
// safe to dereference it.
136+
unsafe { self.ptr.as_ref() }
137+
}
138+
}
139+
129140
impl<T: RefCounted + ?Sized> Drop for Ref<T> {
130141
fn drop(&mut self) {
131142
{
132143
// SAFETY: By the type invariant, there is necessarily a reference to the object.
133144
let obj = unsafe { self.ptr.as_ref() };
134145

135-
// Synchronises with the acquire below or with the acquire in `get_mut`.
136-
if obj.get_count().count.fetch_sub(1, Ordering::Release) != 1 {
146+
// INVARIANT: If the refcount reaches zero, there are no other instances of `Ref`, and
147+
// this instance is being dropped, so the broken invariant is not observable.
148+
// SAFETY: Also by the type invariant, we are allowed to decrement the refcount. We
149+
// also need to limit the scope of `obj` as it cannot be accessed after the refcount is
150+
// decremented.
151+
let is_zero = unsafe { rust_helper_refcount_dec_and_test(obj.get_count().count.get()) };
152+
if !is_zero {
137153
return;
138154
}
139155
}
140156

141-
// Synchronises with the release when decrementing above. This ensures that modifications
142-
// from all previous threads/CPUs are visible to the underlying object's `drop`.
143-
fence(Ordering::Acquire);
144-
145157
// The count reached zero, we must free the memory.
146158
//
147159
// SAFETY: The pointer was initialised from the result of `Box::into_raw`.
@@ -154,7 +166,9 @@ impl<T: RefCounted + ?Sized> Drop for Ref<T> {
154166
/// # Safety
155167
///
156168
/// Implementers of [`RefCounted`] must ensure that all of their constructors call
157-
/// [`Ref::try_new`].
169+
/// [`Ref::try_new`] or [`Ref::try_new_and_init`]. Constructing instances that are not wrapped in
170+
/// [`Ref`] results in unsafety because one could call [`Ref::new_from`], and the destructor would
171+
/// drop the underlying object as if it had been allocated through one of [`Ref`] constructors.
158172
pub unsafe trait RefCounted {
159173
/// Returns a pointer to the object field holds the reference count.
160174
fn get_count(&self) -> &RefCount;
@@ -164,21 +178,7 @@ pub unsafe trait RefCounted {
164178
///
165179
/// It is meant to be embedded in objects to be reference-counted, with [`RefCounted::get_count`]
166180
/// returning a reference to it.
181+
#[derive(Default)]
167182
pub struct RefCount {
168-
count: AtomicUsize,
169-
}
170-
171-
impl RefCount {
172-
/// Constructs a new instance of [`RefCount`].
173-
pub fn new() -> Self {
174-
Self {
175-
count: AtomicUsize::new(1),
176-
}
177-
}
178-
}
179-
180-
impl Default for RefCount {
181-
fn default() -> Self {
182-
Self::new()
183-
}
183+
count: UnsafeCell<bindings::refcount_t>,
184184
}

0 commit comments

Comments
 (0)