xref: /linux/rust/kernel/block/mq/operations.rs (revision 03f76ddff5b04a808ae16c06418460151e2fdd4b)
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