Skip to main content

strat9_kernel/memory/
mapping_index.rs

1//! Reverse mapping index for memory capabilities.
2
3use alloc::collections::BTreeMap;
4
5use smallvec::SmallVec;
6use x86_64::VirtAddr;
7
8use crate::{
9    capability::CapId, memory::address_space::VmaPageSize, process::task::Pid, sync::SpinLock,
10};
11
12/// Reference to a concrete mapping in an address space.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct MappingRef {
15    /// Process that owns the address space.
16    pub pid: Pid,
17    /// Virtual address of the mapping.
18    pub vaddr: VirtAddr,
19    /// Effective page size of the mapping.
20    pub page_size: VmaPageSize,
21}
22
23/// Reverse index from capability ID to live mappings.
24///
25/// The inline capacity of 4 covers the common case (a memory region mapped
26/// in the kernel + up to 3 user address spaces) without heap allocation.
27/// If a capability ever acquires more than 4 mappings, `SmallVec` spills to
28/// the heap while the `SpinLock` is held.  This is not an IRQ path and the
29/// heap lock order (mapping_index → heap) does not conflict with any other
30/// known lock order, so the spill is not a correctness issue : only a minor
31/// latency concern noted in ticket #49.
32pub struct MappingIndex {
33    index: SpinLock<BTreeMap<CapId, SmallVec<[MappingRef; 4]>>>,
34}
35
36impl MappingIndex {
37    /// Creates an empty reverse mapping index.
38    pub fn new() -> Self {
39        Self {
40            index: SpinLock::new(BTreeMap::new()),
41        }
42    }
43
44    /// Registers a mapping for the given capability.
45    pub fn register(&self, cap_id: CapId, mapping: MappingRef) {
46        let mut index = self.index.lock();
47        let mappings = index.entry(cap_id).or_default();
48        if !mappings.iter().any(|existing| *existing == mapping) {
49            mappings.push(mapping);
50        }
51    }
52
53    /// Removes a single mapping for the given capability.
54    pub fn unregister(&self, cap_id: CapId, pid: Pid, vaddr: VirtAddr) {
55        let mut index = self.index.lock();
56        let should_remove = if let Some(mappings) = index.get_mut(&cap_id) {
57            mappings.retain(|mapping| !(mapping.pid == pid && mapping.vaddr == vaddr));
58            mappings.is_empty()
59        } else {
60            false
61        };
62        if should_remove {
63            index.remove(&cap_id);
64        }
65    }
66
67    /// Returns a snapshot of the mappings for the given capability.
68    pub fn lookup(&self, cap_id: CapId) -> SmallVec<[MappingRef; 4]> {
69        self.index.lock().get(&cap_id).cloned().unwrap_or_default()
70    }
71
72    /// Removes and returns every mapping associated with the given capability.
73    pub fn remove_all(&self, cap_id: CapId) -> SmallVec<[MappingRef; 4]> {
74        self.index.lock().remove(&cap_id).unwrap_or_default()
75    }
76}
77
78impl Default for MappingIndex {
79    /// Creates an empty reverse mapping index.
80    fn default() -> Self {
81        Self::new()
82    }
83}