public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] rust: block: safely abstract the blk-mq poll callback
@ 2026-04-10 19:15 ` Wenzhao Liao
  2026-04-10 19:15   ` [RFC PATCH 1/2] rust: block: mq: safely abstract the " Wenzhao Liao
  2026-04-11 10:33   ` [RFC PATCH 0/2] rust: block: safely abstract the blk-mq poll callback Andreas Hindborg
  0 siblings, 2 replies; 4+ messages in thread
From: Wenzhao Liao @ 2026-04-10 19:15 UTC (permalink / raw)
  To: Andreas Hindborg, Jens Axboe, Miguel Ojeda, linux-block,
	rust-for-linux
  Cc: Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel

Rust blk-mq drivers currently cannot implement the `blk_mq_ops->poll`
callback: the Rust vtable hardcodes `poll: None`, and there is no safe
way for a driver to receive the borrowed hardware queue context or the
optional completion batch pointer that blk-mq passes to C drivers.

This small RFC series fills that abstraction gap and wires the new API
into `rnull` as a minimal in-tree user.

The core design choice is to keep the callback on borrowed blk-mq-owned
state instead of exposing raw pointers or synthesizing ownership in
Rust:

  fn poll(
      queue_data: ForeignBorrowed<'_, Self::QueueData>,
      hctx: &HwCtx,
      iob: Option<&IoCompBatch>,
  ) -> PollResult

`HwCtx` is a transparent borrowed wrapper over `struct blk_mq_hw_ctx`,
and `Option<&IoCompBatch>` models the nullable `struct io_comp_batch *`
argument directly. This keeps the Rust side aligned with the C lifetime
model of the callback, while still avoiding raw pointer handling in
drivers.

The return value is represented as a typed `PollResult` instead of a
bare `int`. The enum preserves blk-mq's semantics while making the Rust
side more self-descriptive:

- `PollResult::Completed(u32)` maps to a non-negative completion count.
- `PollResult::Stop` maps to the negative "stop polling" return.

The vtable wiring remains backward compatible: the callback is only
installed when `#[vtable]` reports `HAS_POLL`, so existing Rust blk-mq
drivers still get `poll: None` by default.

Patch 1 introduces the abstraction and hooks it into the blk-mq vtable.
Patch 2 implements a dummy `poll()` method in `rnull` returning
`PollResult::none()` as the minimal reference user.

Runtime validation summary
--------------------------

In addition to compiling the series in an out-of-tree build, the wiring
was validated at runtime in QEMU.

To exercise the Rust poll callback, I used a small out-of-tree helper
module as a test harness. The helper temporarily enabled a poll-capable
queue shape for an `rnull` device, submitted a `REQ_POLLED` bio, and
attached a kprobe directly to the queue's `mq_ops->poll` function
pointer. In the guest, the helper observed the Rust dummy poll callback
being invoked (`hits = 1`) and the polled bio completed successfully,
without deadlocks, crashes, or memory safety issues.

That helper is only test infrastructure and is not part of this RFC
series; it was used solely to provide a minimal "the VTABLE wiring
works" runtime proof for the new abstraction.

Any feedback on the design of the PollResult enum and the borrowing
strategy from hctx would be much appreciated.

Wenzhao Liao (2):
  rust: block: mq: safely abstract the poll callback
  block: rnull: wire up poll queues dummy callback

 drivers/block/rnull/rnull.rs       |   8 ++
 rust/kernel/block/mq.rs            |   2 +-
 rust/kernel/block/mq/operations.rs | 143 +++++++++++++++++++++++++++--
 3 files changed, 146 insertions(+), 7 deletions(-)

-- 
2.34.1

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [RFC PATCH 1/2] rust: block: mq: safely abstract the poll callback
  2026-04-10 19:15 ` [RFC PATCH 0/2] rust: block: safely abstract the blk-mq poll callback Wenzhao Liao
@ 2026-04-10 19:15   ` Wenzhao Liao
  2026-04-10 19:15     ` [RFC PATCH 2/2] block: rnull: wire up poll queues dummy callback Wenzhao Liao
  2026-04-11 10:33   ` [RFC PATCH 0/2] rust: block: safely abstract the blk-mq poll callback Andreas Hindborg
  1 sibling, 1 reply; 4+ messages in thread
From: Wenzhao Liao @ 2026-04-10 19:15 UTC (permalink / raw)
  To: Andreas Hindborg, Jens Axboe, Miguel Ojeda, linux-block,
	rust-for-linux
  Cc: Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel

Add a minimal poll abstraction for Rust blk-mq drivers.

Model the hardware queue context as a shared HwCtx borrow and map the
optional io_comp_batch pointer to Option<&IoCompBatch>. This keeps the
callback on borrowed data owned by blk-mq and avoids exposing raw
pointers in driver implementations.

Use a typed PollResult to represent either a completion count or a
request to stop polling, and wire the callback into the blk-mq vtable
behind HAS_POLL so existing drivers keep poll: None.

Signed-off-by: Wenzhao Liao <wenzhaoliao@ruc.edu.cn>
---
 rust/kernel/block/mq.rs            |   2 +-
 rust/kernel/block/mq/operations.rs | 143 +++++++++++++++++++++++++++--
 2 files changed, 138 insertions(+), 7 deletions(-)

diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs
index 1fd0d54dd549..a66e27401571 100644
--- a/rust/kernel/block/mq.rs
+++ b/rust/kernel/block/mq.rs
@@ -98,6 +98,6 @@
 mod request;
 mod tag_set;
 
-pub use operations::Operations;
+pub use operations::{HwCtx, IoCompBatch, Operations, PollResult};
 pub use request::Request;
 pub use tag_set::TagSet;
diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/operations.rs
index 8ad46129a52c..31e1d61f107d 100644
--- a/rust/kernel/block/mq/operations.rs
+++ b/rust/kernel/block/mq/operations.rs
@@ -10,12 +10,120 @@
     error::{from_result, Result},
     prelude::*,
     sync::{aref::ARef, Refcount},
-    types::ForeignOwnable,
+    types::{ForeignOwnable, Opaque},
 };
 use core::marker::PhantomData;
 
 type ForeignBorrowed<'a, T> = <T as ForeignOwnable>::Borrowed<'a>;
 
+/// A borrowed blk-mq hardware queue context.
+///
+/// # Invariants
+///
+/// [`HwCtx`] is a transparent wrapper around a live `bindings::blk_mq_hw_ctx`.
+#[repr(transparent)]
+pub struct HwCtx(Opaque<bindings::blk_mq_hw_ctx>);
+
+impl HwCtx {
+    /// Creates a reference to an [`HwCtx`] from a valid raw pointer.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `ptr` points to a live
+    /// `bindings::blk_mq_hw_ctx` for the duration of `'a`.
+    ///
+    /// The caller must also ensure that the returned reference is the only
+    /// Rust view used to access the underlying hardware context for the
+    /// duration of `'a`.
+    pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::blk_mq_hw_ctx) -> &'a Self {
+        // SAFETY: `Self` is a transparent wrapper around `bindings::blk_mq_hw_ctx`.
+        unsafe { &*ptr.cast() }
+    }
+
+    /// Returns a raw pointer to the underlying `struct blk_mq_hw_ctx`.
+    pub fn as_ptr(&self) -> *mut bindings::blk_mq_hw_ctx {
+        self as *const Self as *mut bindings::blk_mq_hw_ctx
+    }
+
+    /// Returns the index of this hardware queue.
+    pub fn queue_num(&self) -> u32 {
+        // SAFETY: By the type invariant, `self` wraps a live hardware context.
+        unsafe { (*self.as_ptr()).queue_num }
+    }
+}
+
+/// A borrowed blk-mq completion batching context.
+///
+/// # Invariants
+///
+/// [`IoCompBatch`] is a transparent wrapper around a live
+/// `bindings::io_comp_batch`.
+#[repr(transparent)]
+pub struct IoCompBatch(Opaque<bindings::io_comp_batch>);
+
+impl IoCompBatch {
+    /// Creates an optional reference to an [`IoCompBatch`] from a raw pointer.
+    ///
+    /// # Safety
+    ///
+    /// If `ptr` is non-null, the caller must ensure that it points to a live
+    /// `bindings::io_comp_batch` for the duration of `'a`.
+    ///
+    /// The caller must also ensure that the returned reference is the only
+    /// Rust view used to access the underlying completion batch for the
+    /// duration of `'a`.
+    pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::io_comp_batch) -> Option<&'a Self> {
+        if ptr.is_null() {
+            None
+        } else {
+            // SAFETY: `Self` is a transparent wrapper around `bindings::io_comp_batch`.
+            Some(unsafe { &*ptr.cast() })
+        }
+    }
+
+    /// Returns a raw pointer to the underlying `struct io_comp_batch`.
+    pub fn as_ptr(&self) -> *mut bindings::io_comp_batch {
+        self as *const Self as *mut bindings::io_comp_batch
+    }
+}
+
+/// Result returned from blk-mq poll callbacks.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum PollResult {
+    /// The driver completed the given number of requests.
+    Completed(u32),
+
+    /// The driver cannot make progress and blk-mq should stop polling.
+    Stop,
+}
+
+impl PollResult {
+    /// Returns a result indicating that no requests were completed.
+    pub const fn none() -> Self {
+        Self::Completed(0)
+    }
+
+    /// Returns a result indicating that `completed` requests were completed.
+    pub const fn completed(completed: u32) -> Self {
+        Self::Completed(completed)
+    }
+
+    /// Returns a result indicating that blk-mq should stop polling for now.
+    pub const fn stop() -> Self {
+        Self::Stop
+    }
+
+    fn as_raw(self) -> crate::ffi::c_int {
+        match self {
+            Self::Completed(completed) => {
+                debug_assert!(completed <= crate::ffi::c_int::MAX as u32);
+                completed as crate::ffi::c_int
+            }
+            Self::Stop => -1,
+        }
+    }
+}
+
 /// Implement this trait to interface blk-mq as block devices.
 ///
 /// To implement a block device driver, implement this trait as described in the
@@ -48,7 +156,11 @@ fn queue_rq(
 
     /// Called by the kernel to poll the device for completed requests. Only
     /// used for poll queues.
-    fn poll() -> bool {
+    fn poll(
+        _queue_data: ForeignBorrowed<'_, Self::QueueData>,
+        _hctx: &HwCtx,
+        _iob: Option<&IoCompBatch>,
+    ) -> PollResult {
         build_error!(crate::error::VTABLE_DEFAULT_ERROR)
     }
 }
@@ -168,12 +280,31 @@ impl<T: Operations> OperationsVTable<T> {
     ///
     /// # Safety
     ///
-    /// This function may only be called by blk-mq C infrastructure.
+    /// This function may only be called by blk-mq C infrastructure. The caller
+    /// must ensure that `hctx` is valid. If non-null, `iob` must point to a
+    /// live completion batch for the duration of this callback.
     unsafe extern "C" fn poll_callback(
-        _hctx: *mut bindings::blk_mq_hw_ctx,
-        _iob: *mut bindings::io_comp_batch,
+        hctx: *mut bindings::blk_mq_hw_ctx,
+        iob: *mut bindings::io_comp_batch,
     ) -> crate::ffi::c_int {
-        T::poll().into()
+        // SAFETY: `hctx` is valid as required by this function.
+        let hctx = unsafe { HwCtx::from_raw(hctx) };
+
+        // SAFETY: `hctx` is live as required by this function, so the request
+        // queue and its `queuedata` are also live for the duration of this
+        // callback.
+        let queue_data = unsafe { (*(*hctx.as_ptr()).queue).queuedata };
+
+        // SAFETY: `queue.queuedata` was created by `GenDiskBuilder::build`
+        // with a call to `ForeignOwnable::into_foreign` to create
+        // `queuedata`. `ForeignOwnable::from_foreign` is only called when the
+        // tagset is dropped, which happens after we are dropped.
+        let queue_data = unsafe { T::QueueData::borrow(queue_data) };
+
+        // SAFETY: If non-null, `iob` is valid as required by this function.
+        let iob = unsafe { IoCompBatch::from_raw(iob) };
+
+        T::poll(queue_data, hctx, iob).as_raw()
     }
 
     /// This function is called by the C kernel. A pointer to this function is
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RFC PATCH 2/2] block: rnull: wire up poll queues dummy callback
  2026-04-10 19:15   ` [RFC PATCH 1/2] rust: block: mq: safely abstract the " Wenzhao Liao
@ 2026-04-10 19:15     ` Wenzhao Liao
  0 siblings, 0 replies; 4+ messages in thread
From: Wenzhao Liao @ 2026-04-10 19:15 UTC (permalink / raw)
  To: Andreas Hindborg, Jens Axboe, Miguel Ojeda, linux-block,
	rust-for-linux
  Cc: Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel

Implement the new Operations::poll callback for rnull and return a
zero-completion PollResult.

This keeps rnull's current behavior unchanged while validating that the
borrowed poll abstraction wires cleanly into a Rust blk-mq driver.

Signed-off-by: Wenzhao Liao <wenzhaoliao@ruc.edu.cn>
---
 drivers/block/rnull/rnull.rs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index 0ca8715febe8..a5db14fcac34 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -97,4 +97,12 @@ fn complete(rq: ARef<mq::Request<Self>>) {
             // point, and so `end_ok` cannot fail.
             .expect("Fatal error - expected to be able to end request");
     }
+
+    fn poll(
+        _queue_data: &QueueData,
+        _hctx: &mq::HwCtx,
+        _iob: Option<&mq::IoCompBatch>,
+    ) -> mq::PollResult {
+        mq::PollResult::none()
+    }
 }
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [RFC PATCH 0/2] rust: block: safely abstract the blk-mq poll callback
  2026-04-10 19:15 ` [RFC PATCH 0/2] rust: block: safely abstract the blk-mq poll callback Wenzhao Liao
  2026-04-10 19:15   ` [RFC PATCH 1/2] rust: block: mq: safely abstract the " Wenzhao Liao
@ 2026-04-11 10:33   ` Andreas Hindborg
  1 sibling, 0 replies; 4+ messages in thread
From: Andreas Hindborg @ 2026-04-11 10:33 UTC (permalink / raw)
  To: Wenzhao Liao, Jens Axboe, Miguel Ojeda, linux-block,
	rust-for-linux
  Cc: Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel

"Wenzhao Liao" <wenzhaoliao@ruc.edu.cn> writes:

> Rust blk-mq drivers currently cannot implement the `blk_mq_ops->poll`
> callback: the Rust vtable hardcodes `poll: None`, and there is no safe
> way for a driver to receive the borrowed hardware queue context or the
> optional completion batch pointer that blk-mq passes to C drivers.

Again, this is covered by the series at [1]. Please instruct your AI agent
to read that thread before submitting more patches.

Best regards,
Andreas Hindborg


[1] https://lore.kernel.org/rust-for-linux/20260216-rnull-v6-19-rc5-send-v1-0-de9a7af4b469@kernel.org/


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-04-11 10:34 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <34Y9hHd4E8CdmCmv6LKr2tXYoUyzhgpwOGgK88jxj_B5UHGX-NM-7g0Wr3PXLWBlF0lzMM8lcS_LHtoQAGVn7g==@protonmail.internalid>
2026-04-10 19:15 ` [RFC PATCH 0/2] rust: block: safely abstract the blk-mq poll callback Wenzhao Liao
2026-04-10 19:15   ` [RFC PATCH 1/2] rust: block: mq: safely abstract the " Wenzhao Liao
2026-04-10 19:15     ` [RFC PATCH 2/2] block: rnull: wire up poll queues dummy callback Wenzhao Liao
2026-04-11 10:33   ` [RFC PATCH 0/2] rust: block: safely abstract the blk-mq poll callback Andreas Hindborg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox