Skip to content

Commit 2fd2c86

Browse files
nbdd0121Andreas Hindborg
authored andcommitted
LIST: [PATCH v3 3/4] rust: block: convert block::mq to use Refcount
Currently there's a custom reference counting in `block::mq`, which uses `AtomicU64` Rust atomics, and this type doesn't exist on some 32-bit architectures. We cannot just change it to use 32-bit atomics, because doing so will make it vulnerable to refcount overflow. So switch it to use the kernel refcount `kernel::sync::Refcount` instead. There is an operation needed by `block::mq`, atomically decreasing refcount from 2 to 0, which is not available through refcount.h, so I exposed `Refcount::as_atomic` which allows accessing the refcount directly. Acked-by: Andreas Hindborg <[email protected]> Signed-off-by: Gary Guo <[email protected]> Tested-by: David Gow <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 3206421 commit 2fd2c86

File tree

3 files changed

+40
-51
lines changed

3 files changed

+40
-51
lines changed

rust/kernel/block/mq/operations.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ use crate::{
1010
block::mq::Request,
1111
error::{from_result, Result},
1212
prelude::*,
13+
sync::Refcount,
1314
types::ARef,
1415
};
15-
use core::{marker::PhantomData, sync::atomic::AtomicU64, sync::atomic::Ordering};
16+
use core::marker::PhantomData;
1617

1718
/// Implement this trait to interface blk-mq as block devices.
1819
///
@@ -78,7 +79,7 @@ impl<T: Operations> OperationsVTable<T> {
7879
let request = unsafe { &*(*bd).rq.cast::<Request<T>>() };
7980

8081
// One refcount for the ARef, one for being in flight
81-
request.wrapper_ref().refcount().store(2, Ordering::Relaxed);
82+
request.wrapper_ref().refcount().set(2);
8283

8384
// SAFETY:
8485
// - We own a refcount that we took above. We pass that to `ARef`.
@@ -187,7 +188,7 @@ impl<T: Operations> OperationsVTable<T> {
187188

188189
// SAFETY: The refcount field is allocated but not initialized, so
189190
// it is valid for writes.
190-
unsafe { RequestDataWrapper::refcount_ptr(pdu.as_ptr()).write(AtomicU64::new(0)) };
191+
unsafe { RequestDataWrapper::refcount_ptr(pdu.as_ptr()).write(Refcount::new(0)) };
191192

192193
Ok(0)
193194
})

rust/kernel/block/mq/request.rs

Lines changed: 22 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ use crate::{
88
bindings,
99
block::mq::Operations,
1010
error::Result,
11+
sync::Refcount,
1112
types::{ARef, AlwaysRefCounted, Opaque},
1213
};
1314
use core::{
1415
marker::PhantomData,
1516
ptr::{addr_of_mut, NonNull},
16-
sync::atomic::{AtomicU64, Ordering},
17+
sync::atomic::Ordering,
1718
};
1819

1920
/// A wrapper around a blk-mq [`struct request`]. This represents an IO request.
@@ -37,6 +38,9 @@ use core::{
3738
/// We need to track 3 and 4 to ensure that it is safe to end the request and hand
3839
/// back ownership to the block layer.
3940
///
41+
/// Note that driver can still obtain new `ARef` even if there is no `ARef`s in existence by using
42+
/// `tag_to_rq`, hence the need to distinct B and C.
43+
///
4044
/// The states are tracked through the private `refcount` field of
4145
/// `RequestDataWrapper`. This structure lives in the private data area of the C
4246
/// [`struct request`].
@@ -98,13 +102,17 @@ impl<T: Operations> Request<T> {
98102
///
99103
/// [`struct request`]: srctree/include/linux/blk-mq.h
100104
fn try_set_end(this: ARef<Self>) -> Result<*mut bindings::request, ARef<Self>> {
101-
// We can race with `TagSet::tag_to_rq`
102-
if let Err(_old) = this.wrapper_ref().refcount().compare_exchange(
103-
2,
104-
0,
105-
Ordering::Relaxed,
106-
Ordering::Relaxed,
107-
) {
105+
// To hand back the ownership, we need the current refcount to be 2.
106+
// Since we can race with `TagSet::tag_to_rq`, this needs to atomically reduce
107+
// refcount to 0. `Refcount` does not provide a way to do this, so use the underlying
108+
// atomics directly.
109+
if this
110+
.wrapper_ref()
111+
.refcount()
112+
.as_atomic()
113+
.compare_exchange(2, 0, Ordering::Relaxed, Ordering::Relaxed)
114+
.is_err()
115+
{
108116
return Err(this);
109117
}
110118

@@ -168,13 +176,13 @@ pub(crate) struct RequestDataWrapper {
168176
/// - 0: The request is owned by C block layer.
169177
/// - 1: The request is owned by Rust abstractions but there are no [`ARef`] references to it.
170178
/// - 2+: There are [`ARef`] references to the request.
171-
refcount: AtomicU64,
179+
refcount: Refcount,
172180
}
173181

174182
impl RequestDataWrapper {
175183
/// Return a reference to the refcount of the request that is embedding
176184
/// `self`.
177-
pub(crate) fn refcount(&self) -> &AtomicU64 {
185+
pub(crate) fn refcount(&self) -> &Refcount {
178186
&self.refcount
179187
}
180188

@@ -184,7 +192,7 @@ impl RequestDataWrapper {
184192
/// # Safety
185193
///
186194
/// - `this` must point to a live allocation of at least the size of `Self`.
187-
pub(crate) unsafe fn refcount_ptr(this: *mut Self) -> *mut AtomicU64 {
195+
pub(crate) unsafe fn refcount_ptr(this: *mut Self) -> *mut Refcount {
188196
// SAFETY: Because of the safety requirements of this function, the
189197
// field projection is safe.
190198
unsafe { addr_of_mut!((*this).refcount) }
@@ -200,47 +208,13 @@ unsafe impl<T: Operations> Send for Request<T> {}
200208
// mutate `self` are internally synchronized`
201209
unsafe impl<T: Operations> Sync for Request<T> {}
202210

203-
/// Store the result of `op(target.load())` in target, returning new value of
204-
/// target.
205-
fn atomic_relaxed_op_return(target: &AtomicU64, op: impl Fn(u64) -> u64) -> u64 {
206-
let old = target.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |x| Some(op(x)));
207-
208-
// SAFETY: Because the operation passed to `fetch_update` above always
209-
// return `Some`, `old` will always be `Ok`.
210-
let old = unsafe { old.unwrap_unchecked() };
211-
212-
op(old)
213-
}
214-
215-
/// Store the result of `op(target.load)` in `target` if `target.load() !=
216-
/// pred`, returning [`true`] if the target was updated.
217-
fn atomic_relaxed_op_unless(target: &AtomicU64, op: impl Fn(u64) -> u64, pred: u64) -> bool {
218-
target
219-
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |x| {
220-
if x == pred {
221-
None
222-
} else {
223-
Some(op(x))
224-
}
225-
})
226-
.is_ok()
227-
}
228-
229211
// SAFETY: All instances of `Request<T>` are reference counted. This
230212
// implementation of `AlwaysRefCounted` ensure that increments to the ref count
231213
// keeps the object alive in memory at least until a matching reference count
232214
// decrement is executed.
233215
unsafe impl<T: Operations> AlwaysRefCounted for Request<T> {
234216
fn inc_ref(&self) {
235-
let refcount = &self.wrapper_ref().refcount();
236-
237-
#[cfg_attr(not(CONFIG_DEBUG_MISC), allow(unused_variables))]
238-
let updated = atomic_relaxed_op_unless(refcount, |x| x + 1, 0);
239-
240-
#[cfg(CONFIG_DEBUG_MISC)]
241-
if !updated {
242-
panic!("Request refcount zero on clone")
243-
}
217+
self.wrapper_ref().refcount().inc();
244218
}
245219

246220
unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
@@ -252,10 +226,10 @@ unsafe impl<T: Operations> AlwaysRefCounted for Request<T> {
252226
let refcount = unsafe { &*RequestDataWrapper::refcount_ptr(wrapper_ptr) };
253227

254228
#[cfg_attr(not(CONFIG_DEBUG_MISC), allow(unused_variables))]
255-
let new_refcount = atomic_relaxed_op_return(refcount, |x| x - 1);
229+
let is_zero = refcount.dec_and_test();
256230

257231
#[cfg(CONFIG_DEBUG_MISC)]
258-
if new_refcount == 0 {
232+
if is_zero {
259233
panic!("Request reached refcount zero in Rust abstractions");
260234
}
261235
}

rust/kernel/sync/refcount.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//!
55
//! C header: [`include/linux/refcount.h`](srctree/include/linux/refcount.h)
66
7+
use core::sync::atomic::AtomicI32;
8+
79
use crate::types::Opaque;
810

911
/// Atomic reference counter.
@@ -30,6 +32,18 @@ impl Refcount {
3032
self.0.get()
3133
}
3234

35+
/// Get the underlying atomic counter that backs the refcount.
36+
///
37+
/// NOTE: This will be changed to LKMM atomic in the future.
38+
#[inline]
39+
pub fn as_atomic(&self) -> &AtomicI32 {
40+
let ptr = self.0.get() as *const AtomicI32;
41+
// SAFETY: `refcount_t` is a transparent wrapper of `atomic_t`, which is an atomic 32-bit
42+
// integer that is layout-wise compatible with `AtomicI32`. All values are valid for
43+
// `refcount_t`, despite some of the values are considered saturated and "bad".
44+
unsafe { &*ptr }
45+
}
46+
3347
/// Set a refcount's value.
3448
#[inline]
3549
pub fn set(&self, value: i32) {

0 commit comments

Comments
 (0)