Skip to main content

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::{self, Result},
13    prelude::try_pin_init,
14    types::Opaque,
15};
16use core::{convert::TryInto, marker::PhantomData};
17use 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)]
28pub struct TagSet<T: Operations> {
29    #[pin]
30    inner: Opaque<bindings::blk_mq_tag_set>,
31    _p: PhantomData<T>,
32}
33
34impl<T: Operations> TagSet<T> {
35    /// Try to create a new tag set
36    pub fn new(
37        nr_hw_queues: u32,
38        num_tags: u32,
39        num_maps: u32,
40    ) -> impl PinInit<Self, error::Error> {
41        let tag_set: bindings::blk_mq_tag_set = pin_init::zeroed();
42        let tag_set: Result<_> = core::mem::size_of::<RequestDataWrapper>()
43            .try_into()
44            .map(|cmd_size| {
45                bindings::blk_mq_tag_set {
46                    ops: OperationsVTable::<T>::build(),
47                    nr_hw_queues,
48                    timeout: 0, // 0 means default which is 30Hz in C
49                    numa_node: bindings::NUMA_NO_NODE,
50                    queue_depth: num_tags,
51                    cmd_size,
52                    flags: 0,
53                    driver_data: core::ptr::null_mut::<crate::ffi::c_void>(),
54                    nr_maps: num_maps,
55                    ..tag_set
56                }
57            })
58            .map(Opaque::new)
59            .map_err(|e| e.into());
60
61        try_pin_init!(TagSet {
62            inner <- tag_set.pin_chain(|tag_set| {
63                // SAFETY: we do not move out of `tag_set`.
64                let tag_set: &mut Opaque<_> = unsafe { Pin::get_unchecked_mut(tag_set) };
65                // SAFETY: `tag_set` is a reference to an initialized `blk_mq_tag_set`.
66                error::to_result( unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())})
67            }),
68            _p: PhantomData,
69        })
70    }
71
72    /// Return the pointer to the wrapped `struct blk_mq_tag_set`
73    pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set {
74        self.inner.get()
75    }
76}
77
78#[pinned_drop]
79impl<T: Operations> PinnedDrop for TagSet<T> {
80    fn drop(self: Pin<&mut Self>) {
81        // SAFETY: By type invariant `inner` is valid and has been properly
82        // initialized during construction.
83        unsafe { bindings::blk_mq_free_tag_set(self.inner.get()) };
84    }
85}