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