1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! This module provides the `TagSet` struct to wrap the C `struct blk_mq_tag_set`.
4 //!
5 //! C header: [`include/linux/blk-mq.h`](srctree/include/linux/blk-mq.h)
6 
7 use core::pin::Pin;
8 
9 use crate::{
10     bindings,
11     block::mq::{operations::OperationsVTable, request::RequestDataWrapper, Operations},
12     error,
13     prelude::try_pin_init,
14     types::Opaque,
15 };
16 use core::{convert::TryInto, marker::PhantomData};
17 use pin_init::{pin_data, pinned_drop, PinInit};
18 
19 /// A wrapper for the C `struct blk_mq_tag_set`.
20 ///
21 /// `struct blk_mq_tag_set` contains a `struct list_head` and so must be pinned.
22 ///
23 /// # Invariants
24 ///
25 /// - `inner` is initialized and valid.
26 #[pin_data(PinnedDrop)]
27 #[repr(transparent)]
28 pub struct TagSet<T: Operations> {
29     #[pin]
30     inner: Opaque<bindings::blk_mq_tag_set>,
31     _p: PhantomData<T>,
32 }
33 
34 impl<T: Operations> TagSet<T> {
35     /// Try to create a new tag set
new( nr_hw_queues: u32, num_tags: u32, num_maps: u32, ) -> impl PinInit<Self, error::Error>36     pub fn new(
37         nr_hw_queues: u32,
38         num_tags: u32,
39         num_maps: u32,
40     ) -> impl PinInit<Self, error::Error> {
41         // SAFETY: `blk_mq_tag_set` only contains integers and pointers, which
42         // all are allowed to be 0.
43         let tag_set: bindings::blk_mq_tag_set = unsafe { core::mem::zeroed() };
44         let tag_set = core::mem::size_of::<RequestDataWrapper>()
45             .try_into()
46             .map(|cmd_size| {
47                 bindings::blk_mq_tag_set {
48                     ops: OperationsVTable::<T>::build(),
49                     nr_hw_queues,
50                     timeout: 0, // 0 means default which is 30Hz in C
51                     numa_node: bindings::NUMA_NO_NODE,
52                     queue_depth: num_tags,
53                     cmd_size,
54                     flags: 0,
55                     driver_data: core::ptr::null_mut::<crate::ffi::c_void>(),
56                     nr_maps: num_maps,
57                     ..tag_set
58                 }
59             });
60 
61         try_pin_init!(TagSet {
62             inner <- PinInit::<_, error::Error>::pin_chain(Opaque::new(tag_set?), |tag_set| {
63                 // SAFETY: we do not move out of `tag_set`.
64                 let tag_set = unsafe { Pin::get_unchecked_mut(tag_set) };
65                 // SAFETY: `tag_set` is a reference to an initialized `blk_mq_tag_set`.
66                 error::to_result( unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())})
67             }),
68             _p: PhantomData,
69         })
70     }
71 
72     /// Return the pointer to the wrapped `struct blk_mq_tag_set`
raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set73     pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set {
74         self.inner.get()
75     }
76 }
77 
78 #[pinned_drop]
79 impl<T: Operations> PinnedDrop for TagSet<T> {
drop(self: Pin<&mut Self>)80     fn drop(self: Pin<&mut Self>) {
81         // SAFETY: By type invariant `inner` is valid and has been properly
82         // initialized during construction.
83         unsafe { bindings::blk_mq_free_tag_set(self.inner.get()) };
84     }
85 }
86