1use 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
15pub 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#[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 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#[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 pub fn new() -> Arc<Self> {
130 Arc::new(GpadlMap {
131 map: Default::default(),
132 })
133 }
134
135 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 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 pub fn view(self: Arc<Self>) -> GpadlMapView {
170 GpadlMapView(Some(self))
171 }
172}
173
174#[derive(Debug, Default, Clone)]
176pub struct GpadlMapView(Option<Arc<GpadlMap>>);
177
178#[derive(Debug, Error)]
180#[error("unknown gpadl ID {:#x}", (.0).0)]
181pub struct UnknownGpadlId(GpadlId);
182
183impl GpadlMapView {
184 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}