Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
1 change: 1 addition & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern crate axlog;

pub mod config;
pub mod futex;
mod lrucache;
pub mod mm;
pub mod resources;
pub mod shm;
Expand Down
187 changes: 187 additions & 0 deletions core/src/lrucache.rs
Original file line number Diff line number Diff line change
@@ -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<V, const CAP: usize> {
storage: Vec<CacheNode<V>>,
mru_idx: u16,
lru_idx: u16,
}

/// Internal node for the LRU cache linked list.
#[derive(Debug, Clone)]
struct CacheNode<V> {
payload: V,
prev: u16,
next: u16,
}

impl<V, const CAP: usize> Default for LruCache<V, CAP> {
fn default() -> Self {
Self::new()
}
}

impl<V, const CAP: usize> LruCache<V, CAP> {
/// 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<V> {
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<F>(&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::<V, CAP> {
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<V, CAP>,
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)
}
}
20 changes: 10 additions & 10 deletions core/src/mm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -173,22 +173,22 @@ impl ElfCacheEntry {
}
}

struct ElfLoader(LRUCache<ElfCacheEntry, 32>);
struct ElfLoader(LruCache<ElfCacheEntry, 32>);

type LoadResult = Result<(VirtAddr, Vec<AuxEntry>), Vec<u8>>;

impl ElfLoader {
const fn new() -> Self {
Self(LRUCache::new())
Self(LruCache::new())
}

fn load(&mut self, uspace: &mut AddrSpace, path: &str) -> AxResult<LoadResult> {
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));
Expand All @@ -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
Expand All @@ -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))
Expand Down Expand Up @@ -259,7 +259,7 @@ static ELF_LOADER: Mutex<ElfLoader> = 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.
Expand Down
1 change: 1 addition & 0 deletions local_crates/arm-gic
Submodule arm-gic added at 35bfb5
1 change: 1 addition & 0 deletions local_crates/axcpu
Submodule axcpu added at df2123
1 change: 1 addition & 0 deletions local_crates/axdriver_crates
Submodule axdriver_crates added at cab57c
1 change: 1 addition & 0 deletions local_crates/axplat-aarch64-crosvm-virt
Submodule axplat-aarch64-crosvm-virt added at 3b9ef2
1 change: 1 addition & 0 deletions local_crates/axplat_crates
Submodule axplat_crates added at 28d9b7
1 change: 1 addition & 0 deletions local_crates/fdtree-rs
Submodule fdtree-rs added at d69bcb
1 change: 1 addition & 0 deletions local_crates/kernel_guard
Submodule kernel_guard added at 58b0f7
1 change: 1 addition & 0 deletions local_crates/page_table_multiarch
Submodule page_table_multiarch added at 01df81