mana_driver/
resources.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::bnic_driver::BnicDriver;
5use crate::gdma_driver::GdmaDriver;
6use gdma_defs::GdmaDevId;
7use gdma_defs::GdmaQueueType;
8use std::mem::ManuallyDrop;
9use user_driver::DeviceBacking;
10use user_driver::memory::MemoryBlock;
11
12/// A list of allocated device resources.
13///
14/// The list will be extended by methods that allocate device resources. The
15/// list must be deallocated via a `destroy` method on `Vport` or `ManaDevice`.
16///
17/// If the arena is dropped without calling `destroy`, then device and host
18/// resources will leak.
19#[derive(Default)]
20pub struct ResourceArena {
21    resources: Vec<Resource>,
22}
23
24pub(crate) enum Resource {
25    MemoryBlock(ManuallyDrop<MemoryBlock>),
26    DmaRegion {
27        dev_id: GdmaDevId,
28        gdma_region: u64,
29    },
30    Eq {
31        dev_id: GdmaDevId,
32        eq_id: u32,
33    },
34    BnicQueue {
35        dev_id: GdmaDevId,
36        wq_type: GdmaQueueType,
37        wq_obj: u64,
38    },
39}
40
41impl ResourceArena {
42    /// Creates a new empty resource arena.
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    /// Returns true if the arena has no allocated resources.
48    pub fn is_empty(&self) -> bool {
49        self.resources.is_empty()
50    }
51
52    pub(crate) fn push(&mut self, resource: Resource) {
53        self.resources.push(resource);
54    }
55
56    pub(crate) fn take_dma_region(&mut self, owned_gdma_region: u64) {
57        let i = self
58            .resources
59            .iter()
60            .rposition(|r| matches!(r, Resource::DmaRegion { gdma_region, .. } if *gdma_region == owned_gdma_region))
61            .expect("gdma region must be in arena");
62        self.resources.remove(i);
63    }
64
65    pub(crate) async fn destroy<T: DeviceBacking>(mut self, gdma: &mut GdmaDriver<T>) {
66        for resource in self.resources.drain(..).rev() {
67            let r = match resource {
68                Resource::MemoryBlock(mem) => {
69                    drop(ManuallyDrop::into_inner(mem));
70                    Ok(())
71                }
72                Resource::DmaRegion {
73                    dev_id,
74                    gdma_region,
75                } => gdma.destroy_dma_region(dev_id, gdma_region).await,
76                Resource::Eq { dev_id, eq_id } => gdma.disable_eq(dev_id, eq_id).await,
77                Resource::BnicQueue {
78                    dev_id,
79                    wq_type,
80                    wq_obj,
81                } => {
82                    BnicDriver::new(gdma, dev_id)
83                        .destroy_wq_obj(wq_type, wq_obj)
84                        .await
85                }
86            };
87            if let Err(err) = r {
88                tracing::error!(
89                    error = err.as_ref() as &dyn std::error::Error,
90                    "failed to tear down resource"
91                );
92            }
93        }
94    }
95}
96
97impl Drop for ResourceArena {
98    fn drop(&mut self) {
99        if !self.resources.is_empty() {
100            tracing::error!("leaking resources");
101        }
102    }
103}