diff --git a/Cargo.lock b/Cargo.lock index 122aa957..84db398a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -881,9 +881,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "num-traits", ] @@ -2257,7 +2257,6 @@ dependencies = [ "starry-signal", "starry-vm", "strum", - "uluru", "weak-map", "xmas-elf", ] @@ -2453,15 +2452,6 @@ dependencies = [ "x86", ] -[[package]] -name = "uluru" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da" -dependencies = [ - "arrayvec", -] - [[package]] name = "unicode-ident" version = "1.0.22" diff --git a/core/Cargo.toml b/core/Cargo.toml index 67a43e1d..a58008e8 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -45,7 +45,6 @@ starry-process.workspace = true starry-signal.workspace = true starry-vm.workspace = true strum = { version = "0.27.2", default-features = false, features = ["derive"] } -uluru = "3.1.0" weak-map = "0.1.1" xmas-elf = "0.9" diff --git a/core/src/lib.rs b/core/src/lib.rs index c4e8e09c..9d18ad62 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -12,6 +12,7 @@ extern crate axlog; pub mod config; pub mod futex; +mod lrucache; pub mod mm; pub mod resources; pub mod shm; diff --git a/core/src/lrucache.rs b/core/src/lrucache.rs new file mode 100644 index 00000000..f52812fe --- /dev/null +++ b/core/src/lrucache.rs @@ -0,0 +1,187 @@ +use alloc::vec::Vec; +use core::mem::replace; + +/// A simple LRU Cache implementation based on a fixed-size array. +/// +/// It maintains a fixed-capacity storage and uses an intrusive doubly-linked list +/// structure using array indices to track the usage order (from MRU to LRU). +#[derive(Debug, Clone)] +pub struct LruCache { + storage: Vec>, + mru_idx: u16, + lru_idx: u16, +} + +/// Internal node for the LRU cache linked list. +#[derive(Debug, Clone)] +struct CacheNode { + payload: V, + prev: u16, + next: u16, +} + +impl Default for LruCache { + fn default() -> Self { + Self::new() + } +} + +impl LruCache { + /// Creates a new empty LRU cache. + pub const fn new() -> Self { + Self { + storage: Vec::new(), + mru_idx: 0, + lru_idx: 0, + } + } + + /// Inserts a value into the cache. + /// + /// The inserted value becomes the most-recently-used (MRU) item. + /// If the cache is full, the least-recently-used (LRU) item is removed and returned. + pub fn put(&mut self, val: V) -> Option { + let node = CacheNode { + payload: val, + prev: 0, + next: 0, + }; + + if self.storage.len() >= CAP { + let idx = self.pop_lru(); + let old = replace(&mut self.storage[idx as usize], node); + self.push_mru(idx); + Some(old.payload) + } else { + let idx = self.storage.len() as u16; + self.storage.push(node); + self.push_mru(idx); + None + } + } + + /// Accesses an item in the cache that matches the predicate. + /// + /// If an item is found, it is promoted to the most-recently-used (MRU) position, + /// and the function returns `true`. Otherwise, it returns `false`. + pub fn access(&mut self, mut pred: F) -> bool + where + F: FnMut(&V) -> bool, + { + for i in 0..self.storage.len() { + if pred(&self.storage[i].payload) { + self.promote(i as u16); + return true; + } + } + false + } + + /// Returns a reference to the most-recently-used (MRU) item. + /// + /// This does not change the cache state. + pub fn peek_mru(&self) -> Option<&V> { + self.storage.get(self.mru_idx as usize).map(|n| &n.payload) + } + + /// Returns an iterator over the cache items, ordered from MRU to LRU. + pub fn items(&self) -> LruIter<'_, V, CAP> { + LruIter:: { + cache: self, + pos: self.mru_idx, + } + } + + /// Clears all items from the cache. + pub fn flush(&mut self) { + self.storage.clear(); + self.mru_idx = 0; + self.lru_idx = 0; + } + + fn promote(&mut self, idx: u16) { + if idx != self.mru_idx { + self.unlink(idx); + self.push_mru(idx); + } + } + + fn unlink(&mut self, idx: u16) { + let prev = self.storage[idx as usize].prev; + let next = self.storage[idx as usize].next; + + if idx == self.mru_idx { + self.mru_idx = next; + } else { + self.storage[prev as usize].next = next; + } + + if idx == self.lru_idx { + self.lru_idx = prev; + } else { + self.storage[next as usize].prev = prev; + } + } + + fn push_mru(&mut self, idx: u16) { + if self.storage.len() == 1 { + self.lru_idx = idx; + } else { + self.storage[idx as usize].next = self.mru_idx; + self.storage[self.mru_idx as usize].prev = idx; + } + self.mru_idx = idx; + } + + fn pop_lru(&mut self) -> u16 { + let idx = self.lru_idx; + + if self.storage.len() <= 1 { + // Single-element cache: after popping, there is no valid MRU/LRU. + // Reset indices; the caller is expected to reuse `idx` as needed. + self.mru_idx = 0; + self.lru_idx = 0; + } else { + let prev = self.storage[idx as usize].prev; + // The previous node becomes the new LRU (tail). + self.lru_idx = prev; + // Fully unlink the old LRU by clearing the new tail's `next` + // pointer so it no longer points at the removed node. + self.storage[prev as usize].next = prev; + } + idx + } +} + +/// Iterator over the `LruCache` items, from MRU to LRU. +pub struct LruIter<'a, V, const CAP: usize> { + cache: &'a LruCache, + pos: u16, +} + +impl<'a, V, const CAP: usize> Iterator for LruIter<'a, V, CAP> { + type Item = &'a V; + + fn next(&mut self) -> Option<&'a V> { + // Ensure CAP fits in u16, as indices are u16. + const _: () = assert!(CAP <= u16::MAX as usize, "CAP must fit in u16"); + + if self.cache.storage.is_empty() { + return None; + } + if self.pos as usize >= self.cache.storage.len() { + return None; + } + + let node = &self.cache.storage[self.pos as usize]; + let val = &node.payload; + + let current_pos = self.pos; + if current_pos == self.cache.lru_idx { + self.pos = CAP as u16; + } else { + self.pos = node.next; + } + Some(val) + } +} diff --git a/core/src/mm.rs b/core/src/mm.rs index 6672a81a..90182bee 100644 --- a/core/src/mm.rs +++ b/core/src/mm.rs @@ -20,10 +20,10 @@ use kernel_guard::IrqSave; use memory_addr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr}; use ouroboros::self_referencing; use starry_vm::{VmError, VmIo, VmResult}; -use uluru::LRUCache; use crate::{ config::{USER_SPACE_BASE, USER_SPACE_SIZE}, + lrucache::LruCache, task::AsThread, }; @@ -173,22 +173,22 @@ impl ElfCacheEntry { } } -struct ElfLoader(LRUCache); +struct ElfLoader(LruCache); type LoadResult = Result<(VirtAddr, Vec), Vec>; impl ElfLoader { const fn new() -> Self { - Self(LRUCache::new()) + Self(LruCache::new()) } fn load(&mut self, uspace: &mut AddrSpace, path: &str) -> AxResult { let loc = FS_CONTEXT.lock().resolve(path)?; - if !self.0.touch(|e| e.borrow_cache().location().ptr_eq(&loc)) { + if !self.0.access(|e| e.borrow_cache().location().ptr_eq(&loc)) { match ElfCacheEntry::load(loc)? { Ok(e) => { - self.0.insert(e); + self.0.put(e); } Err(data) => { return Ok(Err(data)); @@ -199,7 +199,7 @@ impl ElfLoader { uspace.clear(); map_trampoline(uspace)?; - let entry = self.0.front().unwrap(); + let entry = self.0.peek_mru().unwrap(); let ldso = if let Some(header) = entry .borrow_elf() .ph @@ -223,12 +223,12 @@ impl ElfLoader { let (elf, ldso) = if let Some(ldso) = ldso { let loc = FS_CONTEXT.lock().resolve(ldso)?; - if !self.0.touch(|e| e.borrow_cache().location().ptr_eq(&loc)) { + if !self.0.access(|e| e.borrow_cache().location().ptr_eq(&loc)) { let e = ElfCacheEntry::load(loc)?.map_err(|_| AxError::InvalidInput)?; - self.0.insert(e); + self.0.put(e); } - let mut iter = self.0.iter(); + let mut iter = self.0.items(); let ldso = iter.next().unwrap(); let elf = iter.next().unwrap(); (elf, Some(ldso)) @@ -259,7 +259,7 @@ static ELF_LOADER: Mutex = Mutex::new(ElfLoader::new()); /// /// Useful for removing noises during memory leak detect. pub fn clear_elf_cache() { - ELF_LOADER.lock().0.clear(); + ELF_LOADER.lock().0.flush(); } /// Load the user app to the user address space. diff --git a/local_crates/arm-gic b/local_crates/arm-gic new file mode 160000 index 00000000..35bfb52d --- /dev/null +++ b/local_crates/arm-gic @@ -0,0 +1 @@ +Subproject commit 35bfb52d71f8e73344178c0537b918b8660b2305 diff --git a/local_crates/axcpu b/local_crates/axcpu new file mode 160000 index 00000000..df212325 --- /dev/null +++ b/local_crates/axcpu @@ -0,0 +1 @@ +Subproject commit df212325150f58978ec1991ce997dc94947a95ae diff --git a/local_crates/axdriver_crates b/local_crates/axdriver_crates new file mode 160000 index 00000000..cab57c73 --- /dev/null +++ b/local_crates/axdriver_crates @@ -0,0 +1 @@ +Subproject commit cab57c73f145350a209d9b1315cb9a805b49f751 diff --git a/local_crates/axplat-aarch64-crosvm-virt b/local_crates/axplat-aarch64-crosvm-virt new file mode 160000 index 00000000..3b9ef265 --- /dev/null +++ b/local_crates/axplat-aarch64-crosvm-virt @@ -0,0 +1 @@ +Subproject commit 3b9ef2651d840ab2ea4e57d16881c16d6aa8e3a8 diff --git a/local_crates/axplat_crates b/local_crates/axplat_crates new file mode 160000 index 00000000..28d9b734 --- /dev/null +++ b/local_crates/axplat_crates @@ -0,0 +1 @@ +Subproject commit 28d9b734049fb02c99fada250a367a7cd40cb58e diff --git a/local_crates/fdtree-rs b/local_crates/fdtree-rs new file mode 160000 index 00000000..d69bcb0e --- /dev/null +++ b/local_crates/fdtree-rs @@ -0,0 +1 @@ +Subproject commit d69bcb0e04176a1c9863cb0f8951b755e45f4a4a diff --git a/local_crates/kernel_guard b/local_crates/kernel_guard new file mode 160000 index 00000000..58b0f7b2 --- /dev/null +++ b/local_crates/kernel_guard @@ -0,0 +1 @@ +Subproject commit 58b0f7b2d03fd06687413f6933d0759323869482 diff --git a/local_crates/page_table_multiarch b/local_crates/page_table_multiarch new file mode 160000 index 00000000..01df8185 --- /dev/null +++ b/local_crates/page_table_multiarch @@ -0,0 +1 @@ +Subproject commit 01df8185b2eb600525b810ca5817db73bb10199d