kernel/block/mq/
tag_set.rs

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
7use core::pin::Pin;
8
9use crate::{
10    bindings,
11    block::mq::{operations::OperationsVTable, request::RequestDataWrapper, Operations},
12    error,
13    prelude::PinInit,
14    try_pin_init,
15    types::Opaque,
16};
17use core::{convert::TryInto, marker::PhantomData};
18use 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)]
29pub struct TagSet<T: Operations> {
30    #[pin]
31    inner: Opaque<bindings::blk_mq_tag_set>,
32    _p: PhantomData<T>,
33}
34
35impl<T: Operations> TagSet<T> {
36    /// Try to create a new tag set
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: 0,
56                    driver_data: core::ptr::null_mut::<crate::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`
74    pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set {
75        self.inner.get()
76    }
77}
78
79#[pinned_drop]
80impl<T: Operations> PinnedDrop for TagSet<T> {
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}