// SPDX-License-Identifier: GPL-2.012//! This module provides an interface for blk-mq drivers to implement.3//!4//! C header: [`include/linux/blk-mq.h`](srctree/include/linux/blk-mq.h)56use crate::{7bindings,8block::mq::{request::RequestDataWrapper, Request},9error::{from_result, Result},10prelude::*,11sync::Refcount,12types::{ARef, ForeignOwnable},13};14use core::marker::PhantomData;1516type ForeignBorrowed<'a, T> = <T as ForeignOwnable>::Borrowed<'a>;1718/// Implement this trait to interface blk-mq as block devices.19///20/// To implement a block device driver, implement this trait as described in the21/// [module level documentation]. The kernel will use the implementation of the22/// functions defined in this trait to interface a block device driver. Note:23/// There is no need for an exit_request() implementation, because the `drop`24/// implementation of the [`Request`] type will be invoked by automatically by25/// the C/Rust glue logic.26///27/// [module level documentation]: kernel::block::mq28#[macros::vtable]29pub trait Operations: Sized {30/// Data associated with the `struct request_queue` that is allocated for31/// the `GenDisk` associated with this `Operations` implementation.32type QueueData: ForeignOwnable;3334/// Called by the kernel to queue a request with the driver. If `is_last` is35/// `false`, the driver is allowed to defer committing the request.36fn queue_rq(37queue_data: ForeignBorrowed<'_, Self::QueueData>,38rq: ARef<Request<Self>>,39is_last: bool,40) -> Result;4142/// Called by the kernel to indicate that queued requests should be submitted.43fn commit_rqs(queue_data: ForeignBorrowed<'_, Self::QueueData>);4445/// Called by the kernel when the request is completed.46fn complete(rq: ARef<Request<Self>>);4748/// Called by the kernel to poll the device for completed requests. Only49/// used for poll queues.50fn poll() -> bool {51build_error!(crate::error::VTABLE_DEFAULT_ERROR)52}53}5455/// A vtable for blk-mq to interact with a block device driver.56///57/// A `bindings::blk_mq_ops` vtable is constructed from pointers to the `extern58/// "C"` functions of this struct, exposed through the `OperationsVTable::VTABLE`.59///60/// For general documentation of these methods, see the kernel source61/// documentation related to `struct blk_mq_operations` in62/// [`include/linux/blk-mq.h`].63///64/// [`include/linux/blk-mq.h`]: srctree/include/linux/blk-mq.h65pub(crate) struct OperationsVTable<T: Operations>(PhantomData<T>);6667impl<T: Operations> OperationsVTable<T> {68/// This function is called by the C kernel. A pointer to this function is69/// installed in the `blk_mq_ops` vtable for the driver.70///71/// # Safety72///73/// - The caller of this function must ensure that the pointee of `bd` is74/// valid for reads for the duration of this function.75/// - This function must be called for an initialized and live `hctx`. That76/// is, `Self::init_hctx_callback` was called and77/// `Self::exit_hctx_callback()` was not yet called.78/// - `(*bd).rq` must point to an initialized and live `bindings:request`.79/// That is, `Self::init_request_callback` was called but80/// `Self::exit_request_callback` was not yet called for the request.81/// - `(*bd).rq` must be owned by the driver. That is, the block layer must82/// promise to not access the request until the driver calls83/// `bindings::blk_mq_end_request` for the request.84unsafe extern "C" fn queue_rq_callback(85hctx: *mut bindings::blk_mq_hw_ctx,86bd: *const bindings::blk_mq_queue_data,87) -> bindings::blk_status_t {88// SAFETY: `bd.rq` is valid as required by the safety requirement for89// this function.90let request = unsafe { &*(*bd).rq.cast::<Request<T>>() };9192// One refcount for the ARef, one for being in flight93request.wrapper_ref().refcount().set(2);9495// SAFETY:96// - We own a refcount that we took above. We pass that to `ARef`.97// - By the safety requirements of this function, `request` is a valid98// `struct request` and the private data is properly initialized.99// - `rq` will be alive until `blk_mq_end_request` is called and is100// reference counted by `ARef` until then.101let rq = unsafe { Request::aref_from_raw((*bd).rq) };102103// SAFETY: `hctx` is valid as required by this function.104let queue_data = unsafe { (*(*hctx).queue).queuedata };105106// SAFETY: `queue.queuedata` was created by `GenDiskBuilder::build` with107// a call to `ForeignOwnable::into_foreign` to create `queuedata`.108// `ForeignOwnable::from_foreign` is only called when the tagset is109// dropped, which happens after we are dropped.110let queue_data = unsafe { T::QueueData::borrow(queue_data) };111112// SAFETY: We have exclusive access and we just set the refcount above.113unsafe { Request::start_unchecked(&rq) };114115let ret = T::queue_rq(116queue_data,117rq,118// SAFETY: `bd` is valid as required by the safety requirement for119// this function.120unsafe { (*bd).last },121);122123if let Err(e) = ret {124e.to_blk_status()125} else {126bindings::BLK_STS_OK as bindings::blk_status_t127}128}129130/// This function is called by the C kernel. A pointer to this function is131/// installed in the `blk_mq_ops` vtable for the driver.132///133/// # Safety134///135/// This function may only be called by blk-mq C infrastructure. The caller136/// must ensure that `hctx` is valid.137unsafe extern "C" fn commit_rqs_callback(hctx: *mut bindings::blk_mq_hw_ctx) {138// SAFETY: `hctx` is valid as required by this function.139let queue_data = unsafe { (*(*hctx).queue).queuedata };140141// SAFETY: `queue.queuedata` was created by `GenDisk::try_new()` with a142// call to `ForeignOwnable::into_foreign()` to create `queuedata`.143// `ForeignOwnable::from_foreign()` is only called when the tagset is144// dropped, which happens after we are dropped.145let queue_data = unsafe { T::QueueData::borrow(queue_data) };146T::commit_rqs(queue_data)147}148149/// This function is called by the C kernel. A pointer to this function is150/// installed in the `blk_mq_ops` vtable for the driver.151///152/// # Safety153///154/// This function may only be called by blk-mq C infrastructure. `rq` must155/// point to a valid request that has been marked as completed. The pointee156/// of `rq` must be valid for write for the duration of this function.157unsafe extern "C" fn complete_callback(rq: *mut bindings::request) {158// SAFETY: This function can only be dispatched through159// `Request::complete`. We leaked a refcount then which we pick back up160// now.161let aref = unsafe { Request::aref_from_raw(rq) };162T::complete(aref);163}164165/// This function is called by the C kernel. A pointer to this function is166/// installed in the `blk_mq_ops` vtable for the driver.167///168/// # Safety169///170/// This function may only be called by blk-mq C infrastructure.171unsafe extern "C" fn poll_callback(172_hctx: *mut bindings::blk_mq_hw_ctx,173_iob: *mut bindings::io_comp_batch,174) -> crate::ffi::c_int {175T::poll().into()176}177178/// This function is called by the C kernel. A pointer to this function is179/// installed in the `blk_mq_ops` vtable for the driver.180///181/// # Safety182///183/// This function may only be called by blk-mq C infrastructure. This184/// function may only be called once before `exit_hctx_callback` is called185/// for the same context.186unsafe extern "C" fn init_hctx_callback(187_hctx: *mut bindings::blk_mq_hw_ctx,188_tagset_data: *mut crate::ffi::c_void,189_hctx_idx: crate::ffi::c_uint,190) -> crate::ffi::c_int {191from_result(|| Ok(0))192}193194/// This function is called by the C kernel. A pointer to this function is195/// installed in the `blk_mq_ops` vtable for the driver.196///197/// # Safety198///199/// This function may only be called by blk-mq C infrastructure.200unsafe extern "C" fn exit_hctx_callback(201_hctx: *mut bindings::blk_mq_hw_ctx,202_hctx_idx: crate::ffi::c_uint,203) {204}205206/// This function is called by the C kernel. A pointer to this function is207/// installed in the `blk_mq_ops` vtable for the driver.208///209/// # Safety210///211/// - This function may only be called by blk-mq C infrastructure.212/// - `_set` must point to an initialized `TagSet<T>`.213/// - `rq` must point to an initialized `bindings::request`.214/// - The allocation pointed to by `rq` must be at the size of `Request`215/// plus the size of `RequestDataWrapper`.216unsafe extern "C" fn init_request_callback(217_set: *mut bindings::blk_mq_tag_set,218rq: *mut bindings::request,219_hctx_idx: crate::ffi::c_uint,220_numa_node: crate::ffi::c_uint,221) -> crate::ffi::c_int {222from_result(|| {223// SAFETY: By the safety requirements of this function, `rq` points224// to a valid allocation.225let pdu = unsafe { Request::wrapper_ptr(rq.cast::<Request<T>>()) };226227// SAFETY: The refcount field is allocated but not initialized, so228// it is valid for writes.229unsafe { RequestDataWrapper::refcount_ptr(pdu.as_ptr()).write(Refcount::new(0)) };230231Ok(0)232})233}234235/// This function is called by the C kernel. A pointer to this function is236/// installed in the `blk_mq_ops` vtable for the driver.237///238/// # Safety239///240/// - This function may only be called by blk-mq C infrastructure.241/// - `_set` must point to an initialized `TagSet<T>`.242/// - `rq` must point to an initialized and valid `Request`.243unsafe extern "C" fn exit_request_callback(244_set: *mut bindings::blk_mq_tag_set,245rq: *mut bindings::request,246_hctx_idx: crate::ffi::c_uint,247) {248// SAFETY: The tagset invariants guarantee that all requests are allocated with extra memory249// for the request data.250let pdu = unsafe { bindings::blk_mq_rq_to_pdu(rq) }.cast::<RequestDataWrapper>();251252// SAFETY: `pdu` is valid for read and write and is properly initialised.253unsafe { core::ptr::drop_in_place(pdu) };254}255256const VTABLE: bindings::blk_mq_ops = bindings::blk_mq_ops {257queue_rq: Some(Self::queue_rq_callback),258queue_rqs: None,259commit_rqs: Some(Self::commit_rqs_callback),260get_budget: None,261put_budget: None,262set_rq_budget_token: None,263get_rq_budget_token: None,264timeout: None,265poll: if T::HAS_POLL {266Some(Self::poll_callback)267} else {268None269},270complete: Some(Self::complete_callback),271init_hctx: Some(Self::init_hctx_callback),272exit_hctx: Some(Self::exit_hctx_callback),273init_request: Some(Self::init_request_callback),274exit_request: Some(Self::exit_request_callback),275cleanup_rq: None,276busy: None,277map_queues: None,278#[cfg(CONFIG_BLK_DEBUG_FS)]279show_rq: None,280};281282pub(crate) const fn build() -> &'static bindings::blk_mq_ops {283&Self::VTABLE284}285}286287288