// SPDX-License-Identifier: GPL-2.0 //! This module provides the `TagSet` struct to wrap the C `struct blk_mq_tag_set`. //! //! C header: [`include/linux/blk-mq.h`](srctree/include/linux/blk-mq.h) use core::pin::Pin; use crate::{ bindings, block::mq::{operations::OperationsVTable, request::RequestDataWrapper, Operations}, error, prelude::try_pin_init, types::Opaque, }; use core::{convert::TryInto, marker::PhantomData}; use pin_init::{pin_data, pinned_drop, PinInit}; /// A wrapper for the C `struct blk_mq_tag_set`. /// /// `struct blk_mq_tag_set` contains a `struct list_head` and so must be pinned. /// /// # Invariants /// /// - `inner` is initialized and valid. #[pin_data(PinnedDrop)] #[repr(transparent)] pub struct TagSet { #[pin] inner: Opaque, _p: PhantomData, } impl TagSet { /// Try to create a new tag set pub fn new( nr_hw_queues: u32, num_tags: u32, num_maps: u32, ) -> impl PinInit { // SAFETY: `blk_mq_tag_set` only contains integers and pointers, which // all are allowed to be 0. let tag_set: bindings::blk_mq_tag_set = unsafe { core::mem::zeroed() }; let tag_set = core::mem::size_of::() .try_into() .map(|cmd_size| { bindings::blk_mq_tag_set { ops: OperationsVTable::::build(), nr_hw_queues, timeout: 0, // 0 means default which is 30Hz in C numa_node: bindings::NUMA_NO_NODE, queue_depth: num_tags, cmd_size, flags: 0, driver_data: core::ptr::null_mut::(), nr_maps: num_maps, ..tag_set } }); try_pin_init!(TagSet { inner <- PinInit::<_, error::Error>::pin_chain(Opaque::new(tag_set?), |tag_set| { // SAFETY: we do not move out of `tag_set`. let tag_set = unsafe { Pin::get_unchecked_mut(tag_set) }; // SAFETY: `tag_set` is a reference to an initialized `blk_mq_tag_set`. error::to_result( unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())}) }), _p: PhantomData, }) } /// Return the pointer to the wrapped `struct blk_mq_tag_set` pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set { self.inner.get() } } #[pinned_drop] impl PinnedDrop for TagSet { fn drop(self: Pin<&mut Self>) { // SAFETY: By type invariant `inner` is valid and has been properly // initialized during construction. unsafe { bindings::blk_mq_free_tag_set(self.inner.get()) }; } }