vmbus_channel/
gpadl.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! GPADL support.
5
6use inspect::Inspect;
7use parking_lot::Mutex;
8use std::collections::HashMap;
9use std::ops::Deref;
10use std::sync::Arc;
11use thiserror::Error;
12pub use vmbus_core::protocol::GpadlId;
13use vmbus_ring::gparange::MultiPagedRangeBuf;
14
15/// Object to call when a GPADL is torn down.
16pub type TeardownFn = Box<dyn FnOnce() + Send>;
17
18struct Teardown(TeardownFn);
19
20impl std::fmt::Debug for Teardown {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        f.pad("Teardown")
23    }
24}
25
26#[derive(Debug)]
27struct GpadlState {
28    tearing_down: bool,
29    map_count: usize,
30    teardown: Option<Teardown>,
31}
32
33#[derive(Debug)]
34struct Gpadl {
35    id: GpadlId,
36    buf: MultiPagedRangeBuf<Vec<u64>>,
37    state: Mutex<GpadlState>,
38}
39
40impl Inspect for Gpadl {
41    fn inspect(&self, req: inspect::Request<'_>) {
42        let state = self.state.lock();
43        req.respond()
44            .field("tearing_down", state.tearing_down)
45            .field("map_count", state.map_count)
46            .child("ranges", |req| {
47                let mut resp = req.respond();
48                for (i, range) in self.buf.iter().enumerate() {
49                    resp.child(&i.to_string(), |req| {
50                        req.respond()
51                            .hex("len", range.len())
52                            .hex("offset", range.offset())
53                            .field(
54                                "pages",
55                                range
56                                    .gpns()
57                                    .iter()
58                                    .map(|gpa| format!("{:x}", gpa))
59                                    .collect::<Vec<_>>()
60                                    .join(" "),
61                            );
62                    });
63                }
64            });
65    }
66}
67
68/// A GPADL that has been provided by the guest. It has not yet been locked.
69///
70/// The guest will not reuse the associated memory while this exists.
71#[derive(Debug)]
72pub struct GpadlView(Arc<Gpadl>);
73
74impl Clone for GpadlView {
75    fn clone(&self) -> Self {
76        let clone = GpadlView(self.0.clone());
77        let mut state = self.0.state.lock();
78        state.map_count += 1;
79        clone
80    }
81}
82
83impl GpadlView {
84    /// Returns the GPADL identifier.
85    pub fn id(&self) -> GpadlId {
86        self.0.id
87    }
88}
89
90impl Deref for GpadlView {
91    type Target = MultiPagedRangeBuf<Vec<u64>>;
92    fn deref(&self) -> &Self::Target {
93        &self.0.buf
94    }
95}
96
97impl Drop for GpadlView {
98    fn drop(&mut self) {
99        let teardown = {
100            let mut state = self.0.state.lock();
101            state.map_count -= 1;
102            if state.map_count == 0 && state.tearing_down {
103                state.teardown.take()
104            } else {
105                None
106            }
107        };
108        if let Some(Teardown(teardown)) = teardown {
109            teardown();
110        }
111    }
112}
113
114/// A set of GPADLs that the guest has made available to the host.
115#[derive(Debug)]
116pub struct GpadlMap {
117    map: Mutex<HashMap<GpadlId, Arc<Gpadl>>>,
118}
119
120impl Inspect for GpadlMap {
121    fn inspect(&self, req: inspect::Request<'_>) {
122        req.respond()
123            .fields("", self.map.lock().iter().map(|(id, gpadl)| (&id.0, gpadl)));
124    }
125}
126
127impl GpadlMap {
128    /// Creates an empty map.
129    pub fn new() -> Arc<Self> {
130        Arc::new(GpadlMap {
131            map: Default::default(),
132        })
133    }
134
135    /// Adds the specified GPADL to the map.
136    pub fn add(&self, id: GpadlId, buf: MultiPagedRangeBuf<Vec<u64>>) {
137        let gpadl = Arc::new(Gpadl {
138            id,
139            buf,
140            state: Mutex::new(GpadlState {
141                tearing_down: false,
142                map_count: 0,
143                teardown: None,
144            }),
145        });
146        let mut map = self.map.lock();
147        map.insert(id, gpadl);
148    }
149
150    /// Removes the specified GPADL from the mapping, calling `f` when there are
151    /// no more [`GpadlView`] instances.
152    pub fn remove(&self, id: GpadlId, f: TeardownFn) -> Option<TeardownFn> {
153        let gpadl = {
154            let mut map = self.map.lock();
155            map.remove(&id).unwrap()
156        };
157        let mut state = gpadl.state.lock();
158        assert!(!state.tearing_down && state.teardown.is_none());
159        state.tearing_down = true;
160        if state.map_count > 0 {
161            state.teardown = Some(Teardown(f));
162            None
163        } else {
164            Some(f)
165        }
166    }
167
168    /// Constructs a GPADL map view.
169    pub fn view(self: Arc<Self>) -> GpadlMapView {
170        GpadlMapView(Some(self))
171    }
172}
173
174/// A GPADL map view for mapping GPADLs.
175#[derive(Debug, Default, Clone)]
176pub struct GpadlMapView(Option<Arc<GpadlMap>>);
177
178/// An error indicating an attempt was made to map an unknown GPADL.
179#[derive(Debug, Error)]
180#[error("unknown gpadl ID {:#x}", (.0).0)]
181pub struct UnknownGpadlId(GpadlId);
182
183impl GpadlMapView {
184    /// Maps the GPADL with `id`.
185    pub fn map(&self, id: GpadlId) -> Result<GpadlView, UnknownGpadlId> {
186        self.try_map(id).ok_or(UnknownGpadlId(id))
187    }
188
189    fn try_map(&self, id: GpadlId) -> Option<GpadlView> {
190        let gpadl = {
191            let map = self.0.as_ref()?.map.lock();
192            map.get(&id)?.clone()
193        };
194        {
195            let mut state = gpadl.state.lock();
196            if state.tearing_down {
197                return None;
198            }
199            state.map_count += 1;
200        }
201        Some(GpadlView(gpadl))
202    }
203}