Add support for the `queue_rqs` callback to the Rust block layer bindings. This callback allows drivers to receive multiple requests in a single call, enabling batch processing optimizations. The callback receives a `RequestList` containing the requests to be processed. Drivers should remove successfully processed requests from the list; any remaining requests will be requeued individually. Signed-off-by: Andreas Hindborg --- rust/kernel/block/mq/operations.rs | 61 +++++++++++++++++++++++++++++++++++++- rust/kernel/block/mq/request.rs | 26 ++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/operations.rs index bccc1903a0d10..e4de6db807e18 100644 --- a/rust/kernel/block/mq/operations.rs +++ b/rust/kernel/block/mq/operations.rs @@ -18,6 +18,8 @@ use core::{marker::PhantomData, ptr::NonNull}; use pin_init::PinInit; +use super::request_list::RequestList; + type ForeignBorrowed<'a, T> = ::Borrowed<'a>; /// Implement this trait to interface blk-mq as block devices. @@ -68,6 +70,15 @@ fn queue_rq( is_poll: bool, ) -> BlkResult; + /// Called by the kernel to queue a list of requests with the driver. + fn queue_rqs( + _hw_data: ForeignBorrowed<'_, Self::HwData>, + _queue_data: ForeignBorrowed<'_, Self::QueueData>, + _requests: &mut RequestList, + ) { + build_error!(crate::error::VTABLE_DEFAULT_ERROR) + } + /// Called by the kernel to indicate that queued requests should be submitted. fn commit_rqs( hw_data: ForeignBorrowed<'_, Self::HwData>, @@ -208,6 +219,50 @@ impl OperationsVTable { } } + /// This function is called by the C kernel to queue a list of new requests. + /// + /// Driver is guaranteed that each request belongs to the same queue. If the + /// driver doesn't empty the `rqlist` completely, then the rest will be + /// queued individually by the block layer upon return. + /// + /// # SAFETY + /// + /// - `requests` must satisfy the safety requirements of `RequestList` + /// - All requests in `requests` must belong to the same hardware context. + unsafe extern "C" fn queue_rqs_callback(requests: *mut bindings::rq_list) { + // SAFETY: + // - By the safety requirements of this function, `requests` is valid for use as a + // `RequestList`. + // - We have exclusive access to `requests` for the duration of this function. + let requests = unsafe { RequestList::from_raw(requests) }; + + let rq_ptr = requests.peek_raw(); + + if rq_ptr.is_null() { + return; + } + + // SAFETY: By function safety requirements, rq_ptr is pointing to a + // valid request. + let hctx = unsafe { (*rq_ptr).mq_hctx }; + + // SAFETY: The safety requirement for this function ensure that `hctx` + // is valid and that `driver_data` was produced by a call to + // `into_foreign` in `Self::init_hctx_callback`. + let hw_data = unsafe { T::HwData::borrow((*hctx).driver_data) }; + + // SAFETY: `hctx` is valid as required by this function. + let queue_data = unsafe { (*(*hctx).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) }; + + T::queue_rqs(hw_data, queue_data, requests); + } + /// This function is called by the C kernel. A pointer to this function is /// installed in the `blk_mq_ops` vtable for the driver. /// @@ -450,7 +505,11 @@ impl OperationsVTable { const VTABLE: bindings::blk_mq_ops = bindings::blk_mq_ops { queue_rq: Some(Self::queue_rq_callback), - queue_rqs: None, + queue_rqs: if T::HAS_QUEUE_RQS { + Some(Self::queue_rqs_callback) + } else { + None + }, commit_rqs: Some(Self::commit_rqs_callback), get_budget: None, put_budget: None, diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request.rs index bc655d202ca01..e22af7ea77bbc 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -148,6 +148,32 @@ pub fn queue(&self) -> &RequestQueue { pub fn as_raw(&self) -> *mut bindings::request { self.0.get() } + + // Return a valid hctx pointer. + fn hctx_raw(&self) -> *mut bindings::blk_mq_hw_ctx { + // SAFETY: The requests is guaranteed to be associated with a hardware + // context while we have access to it. + unsafe { (*self.0.get()).mq_hctx } + } + + /// Get a reference to the [`T::HwData`] for the hardware context that this + /// request is associated with. + pub fn hw_data(&self) -> ::Borrowed<'_> { + let hctx = self.hctx_raw(); + + // SAFETY: `hctx` is valid and `driver_data` was produced by a call to + // `into_foreign` in `Operations::init_hctx_callback`. + unsafe { T::HwData::borrow((*hctx).driver_data) } + } + + pub fn is_poll(&self) -> bool { + let hctx = self.hctx_raw(); + + u32::from( + // SAFETY: `hctx_raw` returns a valid pointer. + unsafe { (*hctx).type_ }, + ) == bindings::hctx_type_HCTX_TYPE_POLL + } } /// A wrapper around a blk-mq [`struct request`]. This represents an IO request. -- 2.51.2