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