1use alloc::collections::BTreeMap;
4
5use smallvec::{smallvec, SmallVec};
6
7use crate::{capability::CapId, sync::SpinLock};
8
9use super::{
10 block::{BlockHandle, BuddyReserved, Exclusive, PhysBlock, Released},
11 block_meta::get_block_meta,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[repr(u8)]
17pub enum BlockState {
18 BuddyReserved = 0,
20 Exclusive = 1,
22 Shared = 2,
24 Free = 3,
26}
27
28#[derive(Debug, Clone)]
30pub struct OwnerEntry {
31 pub state: BlockState,
33 pub refcount: u32,
35 pub caps: SmallVec<[CapId; 4]>,
37 pub transient_refs: u32,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum OwnerError {
44 NotFound,
46 DoubleClaim,
48 CapAlreadyPresent,
50 CapNotFound,
52 StillReferenced,
54 CountOverflow,
56}
57
58#[derive(Debug)]
60pub enum RemoveRefResult {
61 Freed(PhysBlock<Released>),
63 NowExclusive {
65 remaining_cap: CapId,
67 },
68 StillPinned {
70 transient_refs: u32,
72 },
73 StillShared {
75 refcount: u32,
77 },
78}
79
80pub struct OwnershipTable {
82 entries: SpinLock<BTreeMap<BlockHandle, OwnerEntry>>,
83}
84
85impl OwnershipTable {
86 pub fn new() -> Self {
88 Self {
89 entries: SpinLock::new(BTreeMap::new()),
90 }
91 }
92
93 fn sync_meta(handle: BlockHandle, refcount: u32) {
94 let meta = get_block_meta(handle.base);
95 meta.set_order(handle.order);
96 meta.set_refcount(refcount);
97 }
98
99 fn classify_refcount(refcount: u32) -> BlockState {
100 if refcount <= 1 {
101 BlockState::Exclusive
102 } else {
103 BlockState::Shared
104 }
105 }
106
107 fn total_refs(entry: &OwnerEntry) -> Result<u32, OwnerError> {
108 let cap_refs = u32::try_from(entry.caps.len()).map_err(|_| OwnerError::CountOverflow)?;
109 cap_refs
110 .checked_add(entry.transient_refs)
111 .ok_or(OwnerError::CountOverflow)
112 }
113
114 pub fn claim(
116 &self,
117 block: PhysBlock<BuddyReserved>,
118 cap_id: CapId,
119 ) -> Result<PhysBlock<Exclusive>, OwnerError> {
120 let handle = block.handle();
121 let mut entries = self.entries.lock();
122 if entries.contains_key(&handle) {
123 return Err(OwnerError::DoubleClaim);
124 }
125 entries.insert(
126 handle,
127 OwnerEntry {
128 state: BlockState::Exclusive,
129 refcount: 1,
130 caps: smallvec![cap_id],
131 transient_refs: 0,
132 },
133 );
134 Self::sync_meta(handle, 1);
135 Ok(block.into_exclusive())
136 }
137
138 pub fn ensure_ref(&self, handle: BlockHandle, cap_id: CapId) -> Result<BlockState, OwnerError> {
140 let mut entries = self.entries.lock();
141 if let Some(entry) = entries.get_mut(&handle) {
142 if entry.caps.iter().any(|existing| *existing == cap_id) {
143 return Err(OwnerError::CapAlreadyPresent);
144 }
145 entry.caps.push(cap_id);
146 entry.refcount = Self::total_refs(entry)?;
147 entry.state = Self::classify_refcount(entry.refcount);
148 Self::sync_meta(handle, entry.refcount);
149 Ok(entry.state)
150 } else {
151 entries.insert(
152 handle,
153 OwnerEntry {
154 state: BlockState::Exclusive,
155 refcount: 1,
156 caps: smallvec![cap_id],
157 transient_refs: 0,
158 },
159 );
160 Self::sync_meta(handle, 1);
161 Ok(BlockState::Exclusive)
162 }
163 }
164
165 pub fn add_ref(&self, handle: BlockHandle, cap_id: CapId) -> Result<BlockState, OwnerError> {
167 let mut entries = self.entries.lock();
168 let entry = entries.get_mut(&handle).ok_or(OwnerError::NotFound)?;
169 if entry.caps.iter().any(|existing| *existing == cap_id) {
170 return Err(OwnerError::CapAlreadyPresent);
171 }
172
173 entry.caps.push(cap_id);
174 entry.refcount = Self::total_refs(entry)?;
175 entry.state = Self::classify_refcount(entry.refcount);
176 Self::sync_meta(handle, entry.refcount);
177
178 Ok(entry.state)
179 }
180
181 pub fn pin(&self, handle: BlockHandle) -> Result<u32, OwnerError> {
183 let mut entries = self.entries.lock();
184 let entry = entries.get_mut(&handle).ok_or(OwnerError::NotFound)?;
185 entry.transient_refs = entry
186 .transient_refs
187 .checked_add(1)
188 .ok_or(OwnerError::CountOverflow)?;
189 entry.refcount = Self::total_refs(entry)?;
190 entry.state = Self::classify_refcount(entry.refcount);
191 Self::sync_meta(handle, entry.refcount);
192 Ok(entry.refcount)
193 }
194
195 pub fn remove_ref(
197 &self,
198 handle: BlockHandle,
199 cap_id: CapId,
200 ) -> Result<RemoveRefResult, OwnerError> {
201 let mut entries = self.entries.lock();
202 let entry = entries.get_mut(&handle).ok_or(OwnerError::NotFound)?;
203 let position = entry
204 .caps
205 .iter()
206 .position(|existing| *existing == cap_id)
207 .ok_or(OwnerError::CapNotFound)?;
208
209 entry.caps.remove(position);
210 entry.refcount = Self::total_refs(entry)?;
211
212 match entry.refcount {
213 0 => {
214 entries.remove(&handle);
215 Self::sync_meta(handle, 0);
216 Ok(RemoveRefResult::Freed(PhysBlock::from_handle(handle)))
217 }
218 1 => {
219 entry.state = BlockState::Exclusive;
220 Self::sync_meta(handle, entry.refcount);
221 if let Some(&remaining_cap) = entry.caps.first() {
222 Ok(RemoveRefResult::NowExclusive { remaining_cap })
223 } else {
224 Ok(RemoveRefResult::StillPinned {
225 transient_refs: entry.transient_refs,
226 })
227 }
228 }
229 refcount => {
230 entry.state = BlockState::Shared;
231 Self::sync_meta(handle, entry.refcount);
232 Ok(RemoveRefResult::StillShared { refcount })
233 }
234 }
235 }
236
237 pub fn unpin(&self, handle: BlockHandle) -> Result<RemoveRefResult, OwnerError> {
239 let mut entries = self.entries.lock();
240 let entry = entries.get_mut(&handle).ok_or(OwnerError::NotFound)?;
241 if entry.transient_refs == 0 {
242 return Err(OwnerError::CapNotFound);
243 }
244
245 entry.transient_refs -= 1;
246 entry.refcount = Self::total_refs(entry)?;
247
248 match entry.refcount {
249 0 => {
250 entries.remove(&handle);
251 Self::sync_meta(handle, 0);
252 Ok(RemoveRefResult::Freed(PhysBlock::from_handle(handle)))
253 }
254 1 => {
255 entry.state = BlockState::Exclusive;
256 Self::sync_meta(handle, entry.refcount);
257 if let Some(&remaining_cap) = entry.caps.first() {
258 Ok(RemoveRefResult::NowExclusive { remaining_cap })
259 } else {
260 Ok(RemoveRefResult::StillPinned {
261 transient_refs: entry.transient_refs,
262 })
263 }
264 }
265 refcount => {
266 entry.state = BlockState::Shared;
267 Self::sync_meta(handle, entry.refcount);
268 Ok(RemoveRefResult::StillShared { refcount })
269 }
270 }
271 }
272
273 pub fn release(
275 &self,
276 block: PhysBlock<Exclusive>,
277 cap_id: CapId,
278 ) -> Result<PhysBlock<Released>, OwnerError> {
279 match self.remove_ref(block.handle(), cap_id)? {
280 RemoveRefResult::Freed(released) => Ok(released),
281 RemoveRefResult::NowExclusive { .. }
282 | RemoveRefResult::StillPinned { .. }
283 | RemoveRefResult::StillShared { .. } => Err(OwnerError::StillReferenced),
284 }
285 }
286
287 pub fn get(&self, handle: BlockHandle) -> Option<OwnerEntry> {
289 self.entries.lock().get(&handle).cloned()
290 }
291
292 pub fn handle_for_base(&self, base: x86_64::PhysAddr) -> Option<BlockHandle> {
294 self.entries
295 .lock()
296 .keys()
297 .find(|handle| handle.base == base)
298 .copied()
299 }
300
301 pub fn handle_containing(&self, phys: x86_64::PhysAddr) -> Option<BlockHandle> {
303 self.entries.lock().keys().find_map(|handle| {
304 let start = handle.base.as_u64();
305 let end = start.checked_add(handle.size_bytes())?;
306 let addr = phys.as_u64();
307 (addr >= start && addr < end).then_some(*handle)
308 })
309 }
310
311 pub fn refcount(&self, handle: BlockHandle) -> Option<u32> {
313 self.entries.lock().get(&handle).map(|entry| entry.refcount)
314 }
315}
316
317impl Default for OwnershipTable {
318 fn default() -> Self {
320 Self::new()
321 }
322}