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::{self, Result}, 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: Result<_> = 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 .map(Opaque::new) 61 .map_err(|e| e.into()); 62 63 try_pin_init!(TagSet { 64 inner <- tag_set.pin_chain(|tag_set| { 65 // SAFETY: we do not move out of `tag_set`. 66 let tag_set: &mut Opaque<_> = unsafe { Pin::get_unchecked_mut(tag_set) }; 67 // SAFETY: `tag_set` is a reference to an initialized `blk_mq_tag_set`. 68 error::to_result( unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())}) 69 }), 70 _p: PhantomData, 71 }) 72 } 73 74 /// Return the pointer to the wrapped `struct blk_mq_tag_set` raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set75 pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set { 76 self.inner.get() 77 } 78 } 79 80 #[pinned_drop] 81 impl<T: Operations> PinnedDrop for TagSet<T> { drop(self: Pin<&mut Self>)82 fn drop(self: Pin<&mut Self>) { 83 // SAFETY: By type invariant `inner` is valid and has been properly 84 // initialized during construction. 85 unsafe { bindings::blk_mq_free_tag_set(self.inner.get()) }; 86 } 87 } 88