1 // SPDX-License-Identifier: GPL-2.0 2 3 //! This module provides an interface for blk-mq drivers to implement. 4 //! 5 //! C header: [`include/linux/blk-mq.h`](srctree/include/linux/blk-mq.h) 6 7 use crate::{ 8 bindings, 9 block::mq::request::RequestDataWrapper, 10 block::mq::Request, 11 error::{from_result, Result}, 12 prelude::*, 13 sync::Refcount, 14 types::ARef, 15 }; 16 use core::marker::PhantomData; 17 18 /// Implement this trait to interface blk-mq as block devices. 19 /// 20 /// To implement a block device driver, implement this trait as described in the 21 /// [module level documentation]. The kernel will use the implementation of the 22 /// 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 by 25 /// the C/Rust glue logic. 26 /// 27 /// [module level documentation]: kernel::block::mq 28 #[macros::vtable] 29 pub trait Operations: Sized { 30 /// Called by the kernel to queue a request with the driver. If `is_last` is 31 /// `false`, the driver is allowed to defer committing the request. 32 fn queue_rq(rq: ARef<Request<Self>>, is_last: bool) -> Result; 33 34 /// Called by the kernel to indicate that queued requests should be submitted. 35 fn commit_rqs(); 36 37 /// Called by the kernel to poll the device for completed requests. Only 38 /// used for poll queues. 39 fn poll() -> bool { 40 build_error!(crate::error::VTABLE_DEFAULT_ERROR) 41 } 42 } 43 44 /// A vtable for blk-mq to interact with a block device driver. 45 /// 46 /// A `bindings::blk_mq_ops` vtable is constructed from pointers to the `extern 47 /// "C"` functions of this struct, exposed through the `OperationsVTable::VTABLE`. 48 /// 49 /// For general documentation of these methods, see the kernel source 50 /// documentation related to `struct blk_mq_operations` in 51 /// [`include/linux/blk-mq.h`]. 52 /// 53 /// [`include/linux/blk-mq.h`]: srctree/include/linux/blk-mq.h 54 pub(crate) struct OperationsVTable<T: Operations>(PhantomData<T>); 55 56 impl<T: Operations> OperationsVTable<T> { 57 /// This function is called by the C kernel. A pointer to this function is 58 /// installed in the `blk_mq_ops` vtable for the driver. 59 /// 60 /// # Safety 61 /// 62 /// - The caller of this function must ensure that the pointee of `bd` is 63 /// valid for reads for the duration of this function. 64 /// - This function must be called for an initialized and live `hctx`. That 65 /// is, `Self::init_hctx_callback` was called and 66 /// `Self::exit_hctx_callback()` was not yet called. 67 /// - `(*bd).rq` must point to an initialized and live `bindings:request`. 68 /// That is, `Self::init_request_callback` was called but 69 /// `Self::exit_request_callback` was not yet called for the request. 70 /// - `(*bd).rq` must be owned by the driver. That is, the block layer must 71 /// promise to not access the request until the driver calls 72 /// `bindings::blk_mq_end_request` for the request. 73 unsafe extern "C" fn queue_rq_callback( 74 _hctx: *mut bindings::blk_mq_hw_ctx, 75 bd: *const bindings::blk_mq_queue_data, 76 ) -> bindings::blk_status_t { 77 // SAFETY: `bd.rq` is valid as required by the safety requirement for 78 // this function. 79 let request = unsafe { &*(*bd).rq.cast::<Request<T>>() }; 80 81 // One refcount for the ARef, one for being in flight 82 request.wrapper_ref().refcount().set(2); 83 84 // SAFETY: 85 // - We own a refcount that we took above. We pass that to `ARef`. 86 // - By the safety requirements of this function, `request` is a valid 87 // `struct request` and the private data is properly initialized. 88 // - `rq` will be alive until `blk_mq_end_request` is called and is 89 // reference counted by `ARef` until then. 90 let rq = unsafe { Request::aref_from_raw((*bd).rq) }; 91 92 // SAFETY: We have exclusive access and we just set the refcount above. 93 unsafe { Request::start_unchecked(&rq) }; 94 95 let ret = T::queue_rq( 96 rq, 97 // SAFETY: `bd` is valid as required by the safety requirement for 98 // this function. 99 unsafe { (*bd).last }, 100 ); 101 102 if let Err(e) = ret { 103 e.to_blk_status() 104 } else { 105 bindings::BLK_STS_OK as bindings::blk_status_t 106 } 107 } 108 109 /// This function is called by the C kernel. A pointer to this function is 110 /// installed in the `blk_mq_ops` vtable for the driver. 111 /// 112 /// # Safety 113 /// 114 /// This function may only be called by blk-mq C infrastructure. 115 unsafe extern "C" fn commit_rqs_callback(_hctx: *mut bindings::blk_mq_hw_ctx) { 116 T::commit_rqs() 117 } 118 119 /// This function is called by the C kernel. It is not currently 120 /// implemented, and there is no way to exercise this code path. 121 /// 122 /// # Safety 123 /// 124 /// This function may only be called by blk-mq C infrastructure. 125 unsafe extern "C" fn complete_callback(_rq: *mut bindings::request) {} 126 127 /// This function is called by the C kernel. A pointer to this function is 128 /// installed in the `blk_mq_ops` vtable for the driver. 129 /// 130 /// # Safety 131 /// 132 /// This function may only be called by blk-mq C infrastructure. 133 unsafe extern "C" fn poll_callback( 134 _hctx: *mut bindings::blk_mq_hw_ctx, 135 _iob: *mut bindings::io_comp_batch, 136 ) -> crate::ffi::c_int { 137 T::poll().into() 138 } 139 140 /// This function is called by the C kernel. A pointer to this function is 141 /// installed in the `blk_mq_ops` vtable for the driver. 142 /// 143 /// # Safety 144 /// 145 /// This function may only be called by blk-mq C infrastructure. This 146 /// function may only be called once before `exit_hctx_callback` is called 147 /// for the same context. 148 unsafe extern "C" fn init_hctx_callback( 149 _hctx: *mut bindings::blk_mq_hw_ctx, 150 _tagset_data: *mut crate::ffi::c_void, 151 _hctx_idx: crate::ffi::c_uint, 152 ) -> crate::ffi::c_int { 153 from_result(|| Ok(0)) 154 } 155 156 /// This function is called by the C kernel. A pointer to this function is 157 /// installed in the `blk_mq_ops` vtable for the driver. 158 /// 159 /// # Safety 160 /// 161 /// This function may only be called by blk-mq C infrastructure. 162 unsafe extern "C" fn exit_hctx_callback( 163 _hctx: *mut bindings::blk_mq_hw_ctx, 164 _hctx_idx: crate::ffi::c_uint, 165 ) { 166 } 167 168 /// This function is called by the C kernel. A pointer to this function is 169 /// installed in the `blk_mq_ops` vtable for the driver. 170 /// 171 /// # Safety 172 /// 173 /// - This function may only be called by blk-mq C infrastructure. 174 /// - `_set` must point to an initialized `TagSet<T>`. 175 /// - `rq` must point to an initialized `bindings::request`. 176 /// - The allocation pointed to by `rq` must be at the size of `Request` 177 /// plus the size of `RequestDataWrapper`. 178 unsafe extern "C" fn init_request_callback( 179 _set: *mut bindings::blk_mq_tag_set, 180 rq: *mut bindings::request, 181 _hctx_idx: crate::ffi::c_uint, 182 _numa_node: crate::ffi::c_uint, 183 ) -> crate::ffi::c_int { 184 from_result(|| { 185 // SAFETY: By the safety requirements of this function, `rq` points 186 // to a valid allocation. 187 let pdu = unsafe { Request::wrapper_ptr(rq.cast::<Request<T>>()) }; 188 189 // SAFETY: The refcount field is allocated but not initialized, so 190 // it is valid for writes. 191 unsafe { RequestDataWrapper::refcount_ptr(pdu.as_ptr()).write(Refcount::new(0)) }; 192 193 Ok(0) 194 }) 195 } 196 197 /// This function is called by the C kernel. A pointer to this function is 198 /// installed in the `blk_mq_ops` vtable for the driver. 199 /// 200 /// # Safety 201 /// 202 /// - This function may only be called by blk-mq C infrastructure. 203 /// - `_set` must point to an initialized `TagSet<T>`. 204 /// - `rq` must point to an initialized and valid `Request`. 205 unsafe extern "C" fn exit_request_callback( 206 _set: *mut bindings::blk_mq_tag_set, 207 rq: *mut bindings::request, 208 _hctx_idx: crate::ffi::c_uint, 209 ) { 210 // SAFETY: The tagset invariants guarantee that all requests are allocated with extra memory 211 // for the request data. 212 let pdu = unsafe { bindings::blk_mq_rq_to_pdu(rq) }.cast::<RequestDataWrapper>(); 213 214 // SAFETY: `pdu` is valid for read and write and is properly initialised. 215 unsafe { core::ptr::drop_in_place(pdu) }; 216 } 217 218 const VTABLE: bindings::blk_mq_ops = bindings::blk_mq_ops { 219 queue_rq: Some(Self::queue_rq_callback), 220 queue_rqs: None, 221 commit_rqs: Some(Self::commit_rqs_callback), 222 get_budget: None, 223 put_budget: None, 224 set_rq_budget_token: None, 225 get_rq_budget_token: None, 226 timeout: None, 227 poll: if T::HAS_POLL { 228 Some(Self::poll_callback) 229 } else { 230 None 231 }, 232 complete: Some(Self::complete_callback), 233 init_hctx: Some(Self::init_hctx_callback), 234 exit_hctx: Some(Self::exit_hctx_callback), 235 init_request: Some(Self::init_request_callback), 236 exit_request: Some(Self::exit_request_callback), 237 cleanup_rq: None, 238 busy: None, 239 map_queues: None, 240 #[cfg(CONFIG_BLK_DEBUG_FS)] 241 show_rq: None, 242 }; 243 244 pub(crate) const fn build() -> &'static bindings::blk_mq_ops { 245 &Self::VTABLE 246 } 247 } 248