Skip to content

Commit d4cd443

Browse files
lwz23kawasaki
authored andcommitted
rust: block: mq: safely abstract the poll callback
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 <[email protected]>
1 parent 8f17195 commit d4cd443

2 files changed

Lines changed: 138 additions & 7 deletions

File tree

rust/kernel/block/mq.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,6 @@ mod operations;
9898
mod request;
9999
mod tag_set;
100100

101-
pub use operations::Operations;
101+
pub use operations::{HwCtx, IoCompBatch, Operations, PollResult};
102102
pub use request::Request;
103103
pub use tag_set::TagSet;

rust/kernel/block/mq/operations.rs

Lines changed: 137 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,120 @@ use crate::{
1010
error::{from_result, Result},
1111
prelude::*,
1212
sync::{aref::ARef, Refcount},
13-
types::ForeignOwnable,
13+
types::{ForeignOwnable, Opaque},
1414
};
1515
use core::marker::PhantomData;
1616

1717
type ForeignBorrowed<'a, T> = <T as ForeignOwnable>::Borrowed<'a>;
1818

19+
/// A borrowed blk-mq hardware queue context.
20+
///
21+
/// # Invariants
22+
///
23+
/// [`HwCtx`] is a transparent wrapper around a live `bindings::blk_mq_hw_ctx`.
24+
#[repr(transparent)]
25+
pub struct HwCtx(Opaque<bindings::blk_mq_hw_ctx>);
26+
27+
impl HwCtx {
28+
/// Creates a reference to an [`HwCtx`] from a valid raw pointer.
29+
///
30+
/// # Safety
31+
///
32+
/// The caller must ensure that `ptr` points to a live
33+
/// `bindings::blk_mq_hw_ctx` for the duration of `'a`.
34+
///
35+
/// The caller must also ensure that the returned reference is the only
36+
/// Rust view used to access the underlying hardware context for the
37+
/// duration of `'a`.
38+
pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::blk_mq_hw_ctx) -> &'a Self {
39+
// SAFETY: `Self` is a transparent wrapper around `bindings::blk_mq_hw_ctx`.
40+
unsafe { &*ptr.cast() }
41+
}
42+
43+
/// Returns a raw pointer to the underlying `struct blk_mq_hw_ctx`.
44+
pub fn as_ptr(&self) -> *mut bindings::blk_mq_hw_ctx {
45+
self as *const Self as *mut bindings::blk_mq_hw_ctx
46+
}
47+
48+
/// Returns the index of this hardware queue.
49+
pub fn queue_num(&self) -> u32 {
50+
// SAFETY: By the type invariant, `self` wraps a live hardware context.
51+
unsafe { (*self.as_ptr()).queue_num }
52+
}
53+
}
54+
55+
/// A borrowed blk-mq completion batching context.
56+
///
57+
/// # Invariants
58+
///
59+
/// [`IoCompBatch`] is a transparent wrapper around a live
60+
/// `bindings::io_comp_batch`.
61+
#[repr(transparent)]
62+
pub struct IoCompBatch(Opaque<bindings::io_comp_batch>);
63+
64+
impl IoCompBatch {
65+
/// Creates an optional reference to an [`IoCompBatch`] from a raw pointer.
66+
///
67+
/// # Safety
68+
///
69+
/// If `ptr` is non-null, the caller must ensure that it points to a live
70+
/// `bindings::io_comp_batch` for the duration of `'a`.
71+
///
72+
/// The caller must also ensure that the returned reference is the only
73+
/// Rust view used to access the underlying completion batch for the
74+
/// duration of `'a`.
75+
pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::io_comp_batch) -> Option<&'a Self> {
76+
if ptr.is_null() {
77+
None
78+
} else {
79+
// SAFETY: `Self` is a transparent wrapper around `bindings::io_comp_batch`.
80+
Some(unsafe { &*ptr.cast() })
81+
}
82+
}
83+
84+
/// Returns a raw pointer to the underlying `struct io_comp_batch`.
85+
pub fn as_ptr(&self) -> *mut bindings::io_comp_batch {
86+
self as *const Self as *mut bindings::io_comp_batch
87+
}
88+
}
89+
90+
/// Result returned from blk-mq poll callbacks.
91+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
92+
pub enum PollResult {
93+
/// The driver completed the given number of requests.
94+
Completed(u32),
95+
96+
/// The driver cannot make progress and blk-mq should stop polling.
97+
Stop,
98+
}
99+
100+
impl PollResult {
101+
/// Returns a result indicating that no requests were completed.
102+
pub const fn none() -> Self {
103+
Self::Completed(0)
104+
}
105+
106+
/// Returns a result indicating that `completed` requests were completed.
107+
pub const fn completed(completed: u32) -> Self {
108+
Self::Completed(completed)
109+
}
110+
111+
/// Returns a result indicating that blk-mq should stop polling for now.
112+
pub const fn stop() -> Self {
113+
Self::Stop
114+
}
115+
116+
fn as_raw(self) -> crate::ffi::c_int {
117+
match self {
118+
Self::Completed(completed) => {
119+
debug_assert!(completed <= crate::ffi::c_int::MAX as u32);
120+
completed as crate::ffi::c_int
121+
}
122+
Self::Stop => -1,
123+
}
124+
}
125+
}
126+
19127
/// Implement this trait to interface blk-mq as block devices.
20128
///
21129
/// To implement a block device driver, implement this trait as described in the
@@ -48,7 +156,11 @@ pub trait Operations: Sized {
48156

49157
/// Called by the kernel to poll the device for completed requests. Only
50158
/// used for poll queues.
51-
fn poll() -> bool {
159+
fn poll(
160+
_queue_data: ForeignBorrowed<'_, Self::QueueData>,
161+
_hctx: &HwCtx,
162+
_iob: Option<&IoCompBatch>,
163+
) -> PollResult {
52164
build_error!(crate::error::VTABLE_DEFAULT_ERROR)
53165
}
54166
}
@@ -168,12 +280,31 @@ impl<T: Operations> OperationsVTable<T> {
168280
///
169281
/// # Safety
170282
///
171-
/// This function may only be called by blk-mq C infrastructure.
283+
/// This function may only be called by blk-mq C infrastructure. The caller
284+
/// must ensure that `hctx` is valid. If non-null, `iob` must point to a
285+
/// live completion batch for the duration of this callback.
172286
unsafe extern "C" fn poll_callback(
173-
_hctx: *mut bindings::blk_mq_hw_ctx,
174-
_iob: *mut bindings::io_comp_batch,
287+
hctx: *mut bindings::blk_mq_hw_ctx,
288+
iob: *mut bindings::io_comp_batch,
175289
) -> crate::ffi::c_int {
176-
T::poll().into()
290+
// SAFETY: `hctx` is valid as required by this function.
291+
let hctx = unsafe { HwCtx::from_raw(hctx) };
292+
293+
// SAFETY: `hctx` is live as required by this function, so the request
294+
// queue and its `queuedata` are also live for the duration of this
295+
// callback.
296+
let queue_data = unsafe { (*(*hctx.as_ptr()).queue).queuedata };
297+
298+
// SAFETY: `queue.queuedata` was created by `GenDiskBuilder::build`
299+
// with a call to `ForeignOwnable::into_foreign` to create
300+
// `queuedata`. `ForeignOwnable::from_foreign` is only called when the
301+
// tagset is dropped, which happens after we are dropped.
302+
let queue_data = unsafe { T::QueueData::borrow(queue_data) };
303+
304+
// SAFETY: If non-null, `iob` is valid as required by this function.
305+
let iob = unsafe { IoCompBatch::from_raw(iob) };
306+
307+
T::poll(queue_data, hctx, iob).as_raw()
177308
}
178309

179310
/// This function is called by the C kernel. A pointer to this function is

0 commit comments

Comments
 (0)