[PATCH 07/79] block: rust: allow `hrtimer::Timer` in `RequestData`

Andreas Hindborg
2026-02-15T23:47:05Z

`Request` is essentially a smart pointer that derefs to
`Operations::RequestData`. To use an `HrTimer` in `Operations::RequestData`
via the `Request` pointer, we must implement `HrTimerPointer` for
`Request`.

Thus, implement `HrTimerPointer` and friends for `ARef<Request>`.

Publicly export `HrTimer::raw_cancel` and `HrTimer::into_c`.

Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/block/mq.rs | 1 +
rust/kernel/block/mq/request.rs | 138 +++++++++++++++++++++++++++++++++++++++-
rust/kernel/time/hrtimer.rs | 5 +-
3 files changed, 141 insertions(+), 3 deletions(-)

diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs
index a285b753ada88..cedaf85ba0b1d 100644
--- a/rust/kernel/block/mq.rs
+++ b/rust/kernel/block/mq.rs
@@ -108,4 +108,5 @@

pub use operations::Operations;
pub use request::Request;
+pub use request::RequestTimerHandle;
pub use tag_set::TagSet;
diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request.rs
index 8a6c29ac627ee..f270060be27eb 100644
--- a/rust/kernel/block/mq/request.rs
+++ b/rust/kernel/block/mq/request.rs
@@ -12,9 +12,12 @@
atomic::ordering,
Refcount,
},
+ time::hrtimer::{
+ HasHrTimer, HrTimer, HrTimerCallback, HrTimerHandle, HrTimerMode, HrTimerPointer,
+ },
types::{Opaque, Ownable, OwnableRefCounted, Owned},
};
-use core::{marker::PhantomData, ptr::NonNull};
+use core::{ffi::c_void, marker::PhantomData, ptr::NonNull};

/// A wrapper around a blk-mq [`struct request`]. This represents an IO request.
///
@@ -116,6 +119,11 @@ pub(crate) fn wrapper_ref(&self) -> &RequestDataWrapper<T> {
// valid as a shared reference.
unsafe { Self::wrapper_ptr(core::ptr::from_ref(self).cast_mut()).as_ref() }
}
+
+ /// Return a reference to the per-request data associated with this request.
+ pub fn data_ref(&self) -> &T::RequestData {
+ &self.wrapper_ref().data
+ }
}

/// A wrapper around data stored in the private area of the C [`struct request`].
@@ -300,3 +308,131 @@ fn into_shared(this: Owned<Self>) -> ARef<Self> {
unsafe { ARef::from_raw(Owned::into_raw(this)) }
}
}
+
+/// A handle for a timer that is embedded in a [`Request`] private data area.
+pub struct RequestTimerHandle<T>
+where
+ T: Operations,
+ T::RequestData: HasHrTimer<T::RequestData>,
+{
+ inner: ARef<Request<T>>,
+}
+
+// SAFETY: The drop implementation of `RequestTimerHandle` calls `cancel`, which cancels the timer
+// if it is running. `drop` will block if the timer handler is running. This is ensured via a call
+// to `HrTimer::raw_cancel` in the implementation of `cancel`.
+unsafe impl<T> HrTimerHandle for RequestTimerHandle<T>
+where
+ T: Operations,
+ T::RequestData: HasHrTimer<T::RequestData>,
+{
+ fn cancel(&mut self) -> bool {
+ let request_data_ptr = &self.inner.wrapper_ref().data as *const T::RequestData;
+
+ // SAFETY: As we obtained `self_ptr` from a valid reference above, it
+ // must point to a valid `U`.
+ let timer_ptr = unsafe {
+ <T::RequestData as HasHrTimer<T::RequestData>>::raw_get_timer(request_data_ptr)
+ };
+
+ // SAFETY: As `timer_ptr` points into `U` and `U` is valid, `timer_ptr`
+ // must point to a valid `HrTimer` instance.
+ unsafe { HrTimer::<T::RequestData>::raw_cancel(timer_ptr) }
+ }
+}
+
+impl<T> RequestTimerHandle<T>
+where
+ T: Operations,
+ T::RequestData: HasHrTimer<T::RequestData>,
+{
+ /// Drop the timer handle without cancelling the timer.
+ ///
+ /// This is safe because dropping the last [`ARef<Request>`] does not drop the [`Request`].
+ pub fn dismiss(mut self) {
+ let inner = core::ptr::from_mut(&mut self.inner);
+
+ // SAFETY: `inner` is valid for reads and writes, is properly aligned and nonnull. We have
+ // exclusive access to `inner` and we do not access `inner` after this call.
+ unsafe { core::ptr::drop_in_place(inner) };
+ core::mem::forget(self);
+ }
+}
+
+impl<T> Drop for RequestTimerHandle<T>
+where
+ T: Operations,
+ T::RequestData: HasHrTimer<T::RequestData>,
+{
+ fn drop(&mut self) {
+ self.cancel();
+ }
+}
+
+impl<T> HrTimerPointer for ARef<Request<T>>
+where
+ T: Operations,
+ T::RequestData: HasHrTimer<T::RequestData>,
+ T::RequestData: Sync,
+{
+ type TimerMode = <T::RequestData as HasHrTimer<T::RequestData>>::TimerMode;
+ type TimerHandle = RequestTimerHandle<T>;
+
+ fn start(self, expires: <Self::TimerMode as HrTimerMode>::Expires) -> RequestTimerHandle<T> {
+ let pdu_ptr = self.data_ref() as *const T::RequestData;
+
+ // SAFETY: `pdu_pointer` is coerced from a live reference to a `T` and this points to a
+ // valid `T`. The reference is valid until `T` is dropped, and the timer will be canceled
+ // before this.
+ unsafe { T::RequestData::start(pdu_ptr, expires) };
+
+ RequestTimerHandle { inner: self }
+ }
+}
+
+impl<T> kernel::time::hrtimer::RawHrTimerCallback for ARef<Request<T>>
+where
+ T: Operations,
+ T::RequestData: HasHrTimer<T::RequestData>,
+ T::RequestData: for<'a> HrTimerCallback<Pointer<'a> = ARef<Request<T>>>,
+ T::RequestData: Sync,
+{
+ type CallbackTarget<'a> = Self;
+
+ unsafe extern "C" fn run(ptr: *mut bindings::hrtimer) -> bindings::hrtimer_restart {
+ // `HrTimer` is `repr(transparent)`
+ let timer_ptr = ptr.cast::<kernel::time::hrtimer::HrTimer<T::RequestData>>();
+
+ // SAFETY: By C API contract `ptr` is the pointer we passed when
+ // enqueuing the timer, so it is a `HrTimer<T::RequestData>` embedded in a `T::RequestData`
+ let request_data_ptr = unsafe { T::RequestData::timer_container_of(timer_ptr) };
+
+ let offset = core::mem::offset_of!(RequestDataWrapper<T>, data);
+
+ // SAFETY: This sub stays within the `bindings::request` allocation and does not wrap.
+ let pdu_ptr = unsafe {
+ request_data_ptr
+ .cast::<u8>()
+ .sub(offset)
+ .cast::<RequestDataWrapper<T>>()
+ };
+
+ // SAFETY: This request pointer was passed to us by the kernel in `init_request_callback`.
+ let request_ptr = unsafe { bindings::blk_mq_rq_from_pdu(pdu_ptr.cast::<c_void>()) };
+
+ // SAFETY: By C API contract, we have ownership of the request.
+ let request_ref = unsafe { &*(request_ptr as *const Request<T>) };
+
+ request_ref.inc_ref();
+ // SAFETY: We just incremented the refcount above.
+ let aref: ARef<Request<T>> = unsafe { ARef::from_raw(NonNull::from(request_ref)) };
+
+ // SAFETY:
+ // - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so
+ // it is a valid pointer to a `HrTimer<T>` embedded in a `T`.
+ // - We are within `RawHrTimerCallback::run`
+ let context = unsafe { kernel::time::hrtimer::HrTimerCallbackContext::from_raw(timer_ptr) };
+
+ T::RequestData::run(aref, context).into_c()
+ }
+}
diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs
index b2272b059e504..0512a8dfee82a 100644
--- a/rust/kernel/time/hrtimer.rs
+++ b/rust/kernel/time/hrtimer.rs
@@ -160,7 +160,7 @@ unsafe fn raw_get(this: *const Self) -> *mut bindings::hrtimer {
/// # Safety
///
/// `this` must point to a valid `Self`.
- pub(crate) unsafe fn raw_cancel(this: *const Self) -> bool {
+ pub unsafe fn raw_cancel(this: *const Self) -> bool {
// SAFETY: `this` points to an allocation of at least `HrTimer` size.
let c_timer_ptr = unsafe { HrTimer::raw_get(this) };

@@ -514,7 +514,8 @@ pub enum HrTimerRestart {
}

impl HrTimerRestart {
- fn into_c(self) -> bindings::hrtimer_restart {
+ /// Convert `self` into an integer for FFI use.
+ pub fn into_c(self) -> bindings::hrtimer_restart {
self as bindings::hrtimer_restart
}
}

--
2.51.2