[PATCH 69/79] block: rust: add `TagSet::tag_to_rq`

Andreas Hindborg
2026-02-15T23:44:16Z

Add a way for block device drivers to obtain a `Request` from a tag. This
is backed by the C `blk_mq_tag_to_rq` but with added checks to ensure
memory safety.

Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/helpers/blk.c | 6 ++++
rust/kernel/block/mq/tag_set.rs | 72 ++++++++++++++++++++++++++++++++++++++---
2 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/rust/helpers/blk.c b/rust/helpers/blk.c
index 0db567dfc5d53..bb77af6dca9c8 100644
--- a/rust/helpers/blk.c
+++ b/rust/helpers/blk.c
@@ -53,3 +53,9 @@ __rust_helper struct request *rust_helper_rq_list_peek(struct rq_list *rl)
{
return rq_list_peek(rl);
}
+
+__rust_helper struct request *
+rust_helper_blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag)
+{
+ return blk_mq_tag_to_rq(tags, tag);
+}
diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set.rs
index 47a162360e628..e7d7217da8922 100644
--- a/rust/kernel/block/mq/tag_set.rs
+++ b/rust/kernel/block/mq/tag_set.rs
@@ -5,12 +5,12 @@
//! C header: [`include/linux/blk-mq.h`](srctree/include/linux/blk-mq.h)

use crate::{
- bindings,
block::mq::{operations::OperationsVTable, request::RequestDataWrapper, Operations},
error::{self, Result},
- prelude::*,
- try_pin_init,
- types::{ForeignOwnable, Opaque},
+ pr_warn,
+ prelude::{ENOMEM, *},
+ sync::atomic::ordering,
+ types::{ARef, ForeignOwnable, Opaque},
};
use core::{convert::TryInto, marker::PhantomData, pin::Pin};
use pin_init::{pin_data, pinned_drop, PinInit};
@@ -19,6 +19,8 @@
pub use flags::Flag;
pub use flags::Flags;

+use super::Request;
+
/// A wrapper for the C `struct blk_mq_tag_set`.
///
/// `struct blk_mq_tag_set` contains a `struct list_head` and so must be pinned.
@@ -166,6 +168,68 @@ pub fn data(&self) -> <T::TagSetData as ForeignOwnable>::Borrowed<'_> {
// converted back with `from_foreign` while `&self` is live.
unsafe { T::TagSetData::borrow(ptr) }
}
+
+ /// Obtain a shared reference to a request.
+ ///
+ /// This method will hang if the request is not owned by the driver, or if
+ /// the driver holds an [`Ownable<Request>`] reference to the request.
+ pub fn tag_to_rq(&self, qid: u32, tag: u32) -> Option<ARef<Request<T>>> {
+ if qid >= self.hw_queue_count() {
+ // TODO: Use pr_warn_once!
+ pr_warn!("Invalid queue id: {qid}\n");
+ return None;
+ }
+
+ // SAFETY: We checked that `qid` is within bounds.
+ let tags = unsafe { *(*self.inner.get()).tags.add(qid as usize) };
+
+ // SAFETY: We checked `qid` for overflow above, so `tags` is valid.
+ let rq_ptr = unsafe { bindings::blk_mq_tag_to_rq(tags, tag) };
+ if rq_ptr.is_null() {
+ None
+ } else {
+ // SAFETY: if `rq_ptr`is not null, it is a valid request pointer.
+ let refcount_ptr = unsafe {
+ RequestDataWrapper::refcount_ptr(
+ Request::wrapper_ptr(rq_ptr.cast::<Request<T>>()).as_ptr(),
+ )
+ };
+
+ // SAFETY: The refcount was initialized in `init_request_callback` and is never
+ // referenced mutably.
+ let refcount_ref = unsafe { &*refcount_ptr };
+
+ let atomic_ref = refcount_ref.as_atomic();
+
+ // It is possible for an interrupt to arrive faster than the last
+ // change to the refcount, so retry if the refcount is not what we
+ // think it should be.
+ loop {
+ // Load acquire to sync with store release of `Owned<Request>`
+ // being destroyed (prevent mutable access overlapping shared
+ // access).
+ let prev = atomic_ref.load(ordering::Acquire);
+
+ if prev >= 1 {
+ // Store relaxed as no other operations need to happen strictly
+ // before or after the increment.
+ match atomic_ref.cmpxchg(prev, prev + 1, ordering::Relaxed) {
+ Ok(_) => break,
+ // NOTE: We cannot use the load part of a failed cmpxchg as it is always
+ // relaxed.
+ Err(_) => continue,
+ }
+ } else {
+ // We are probably waiting to observe a refcount increment.
+ core::hint::spin_loop();
+ continue;
+ };
+ }
+
+ // SAFETY: We checked above that `rq_ptr` is valid for use as an `ARef`.
+ Some(unsafe { Request::aref_from_raw(rq_ptr) })
+ }
+ }
}

#[pinned_drop]

--
2.51.2