starnix_core/mm/
map_info_cache.rs1use crate::task::CurrentTask;
6use memory_pinning::ShadowProcess;
7use page_buf::PageBuf;
8use starnix_sync::{LockDepMutex, MapInfoCacheBufLock};
9use starnix_uapi::errors::Errno;
10use starnix_uapi::from_status_like_fdio;
11use std::sync::Arc;
12
13pub struct MapInfoCache {
20 state: LockDepMutex<MapInfoCacheState, MapInfoCacheBufLock>,
21}
22
23struct MapInfoCacheState {
24 buf: PageBuf<zx::MapInfo>,
25 scratch_pool: Vec<Vec<zx::MapInfo>>,
34}
35
36const ZIRCON_NAME: zx::Name = zx::Name::new_lossy("starnix_zx_map_info_cache");
37const MAX_SCRATCH_POOL_LEN: usize = 4;
38
39impl MapInfoCache {
40 pub fn get_or_init(current_task: &CurrentTask) -> Result<Arc<Self>, Errno> {
41 let kernel = current_task.kernel();
42 kernel.expando.get_or_try_init(|| {
43 let pinned_shadow_process = kernel.expando.get_or_try_init(|| {
44 ShadowProcess::new(ZIRCON_NAME)
45 .map(InfoCacheShadowProcess)
46 .map_err(|e| from_status_like_fdio!(e))
47 })?;
48
49 let num_cache_elements = kernel.features.cached_zx_map_info_bytes as usize
50 / std::mem::size_of::<zx::MapInfo>();
51 Self::new(&pinned_shadow_process.0, num_cache_elements)
52 })
53 }
54
55 fn new(shadow_process: &ShadowProcess, num_cache_elements: usize) -> Result<Self, Errno> {
56 let buf = PageBuf::new_with_extra_vmar(num_cache_elements, shadow_process.vmar())
57 .map_err(|e| from_status_like_fdio!(e))?;
58 buf.set_name(&ZIRCON_NAME);
59
60 Ok(Self { state: MapInfoCacheState { buf, scratch_pool: Vec::new() }.into() })
61 }
62
63 pub fn with_map_infos<R>(
64 &self,
65 vmar: &zx::Vmar,
66 op: impl FnOnce(Result<&[zx::MapInfo], zx::Status>) -> R,
67 ) -> R {
68 let mut state = self.state.lock();
69 let mut scratch = state.scratch_pool.pop().unwrap_or_default();
70 match vmar.maps(state.buf.as_mut()) {
71 Ok((maps, _, avail)) if maps.len() == avail => {
72 scratch.clear();
73 scratch.extend_from_slice(maps);
74 drop(state);
75 let res = op(Ok(&scratch));
76 let mut state = self.state.lock();
77 if state.scratch_pool.len() < MAX_SCRATCH_POOL_LEN {
78 state.scratch_pool.push(scratch);
79 }
80 return res;
81 }
82 Err(e) => {
83 if state.scratch_pool.len() < MAX_SCRATCH_POOL_LEN && scratch.capacity() > 0 {
84 state.scratch_pool.push(scratch);
85 }
86 drop(state);
87 return op(Err(e));
88 }
89
90 Ok(_) => {
92 if state.scratch_pool.len() < MAX_SCRATCH_POOL_LEN && scratch.capacity() > 0 {
93 state.scratch_pool.push(scratch);
94 }
95 }
96 }
97 drop(state);
99
100 match vmar.maps_vec() {
101 Ok(maps) => op(Ok(&maps)),
102 Err(e) => op(Err(e)),
103 }
104 }
105}
106
107pub struct InfoCacheShadowProcess(ShadowProcess);
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[fuchsia::test]
118 async fn basic_test() {
119 let shadow_process = ShadowProcess::new(zx::Name::new_lossy("testing123")).unwrap();
120 let cache = MapInfoCache::new(&shadow_process, 100).unwrap();
121 let maps_len = cache.with_map_infos(&shadow_process.vmar(), |maps| {
122 let maps = maps.unwrap();
123 assert_ne!(maps, &[]);
124 maps.len()
125 });
126 assert!(maps_len > 0);
127 assert_eq!(cache.state.lock().scratch_pool.len(), 1);
128 }
129
130 #[fuchsia::test]
131 async fn fall_back_to_heap_over_limit() {
132 let shadow_process = ShadowProcess::new(zx::Name::new_lossy("testing123")).unwrap();
133 let cache = MapInfoCache::new(&shadow_process, 1).unwrap();
134
135 let number_of_extra_mappings =
138 (zx::system_get_page_size() as usize / std::mem::size_of::<zx::MapInfo>()) * 2;
139 let vmo_to_map = zx::Vmo::create(4096).unwrap();
140 for _ in 0..number_of_extra_mappings {
141 fuchsia_runtime::vmar_root_self()
142 .map(0, &vmo_to_map, 0, 4096, zx::VmarFlags::PERM_READ)
143 .unwrap();
144 }
145
146 let maps_base_addr = cache.with_map_infos(&fuchsia_runtime::vmar_root_self(), |maps| {
147 let maps = maps.unwrap();
148 assert!(!maps.is_empty());
149 maps.as_ptr() as usize
150 });
151
152 let cache_base_addr = cache.state.lock().buf.as_mut().as_ptr() as usize;
153 assert_ne!(maps_base_addr, cache_base_addr, "map infos must not have been read into cache");
154 }
155}