From 3b6629e58f16ee400a8556af8b16447bb1a6eb00 Mon Sep 17 00:00:00 2001 From: bunnie Date: Wed, 19 Jun 2024 21:28:58 +0800 Subject: [PATCH] add userland call for requesting free memory also refactor the startup API to no longer create and store deprecated elements --- services/xous-swapper/src/lib.rs | 55 +++++++++++++++++++++++++++++++ services/xous-swapper/src/main.rs | 54 +++++++++++++++++------------- 2 files changed, 87 insertions(+), 22 deletions(-) create mode 100644 services/xous-swapper/src/lib.rs diff --git a/services/xous-swapper/src/lib.rs b/services/xous-swapper/src/lib.rs new file mode 100644 index 000000000..d51689141 --- /dev/null +++ b/services/xous-swapper/src/lib.rs @@ -0,0 +1,55 @@ +/// public userspace & swapper handler -> swapper userspace ABI +#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive)] +#[repr(usize)] +pub enum Opcode { + /// Userspace request to GC some physical pages + GarbageCollect, + /// Test messages + #[cfg(feature = "swap-userspace-testing")] + Test0, + None, +} + +pub const SWAPPER_PUBLIC_NAME: &'static str = "_swapper server_"; + +pub struct Swapper { + conn: xous::CID, +} +impl Swapper { + pub fn new() -> Result { + let xns = xous_api_names::XousNames::new().expect("couldn't connect to xous names"); + REFCOUNT.fetch_add(1, Ordering::Relaxed); + let conn = + xns.request_connection_blocking(SWAPPER_PUBLIC_NAME).expect("Can't connect to TRNG server"); + Ok(Swapper { conn }) + } + + /// Attempts to free `page_count` pages of RAM. + pub fn garbage_collect_pages(&self, page_count: usize) -> usize { + match xous::send_message( + self.conn, + xous::Message::new_blocking_scalar(Opcode::GarbageCollect as usize, page_count, 0, 0, 0), + ) { + Ok(xous::Result::Scalar5(free_pages, _, _, _, _)) => free_pages, + _e => { + log::warn!("Garbage collect call failed with internal error: {:?}", _e); + 0 + } + } + // no result is given, but the call blocks until the GC call has completed in the swapper. + } +} + +use core::sync::atomic::{AtomicU32, Ordering}; +static REFCOUNT: AtomicU32 = AtomicU32::new(0); +impl Drop for Swapper { + fn drop(&mut self) { + // de-allocate myself. It's unsafe because we are responsible to make sure nobody else is using the + // connection. + if REFCOUNT.fetch_sub(1, Ordering::Relaxed) == 1 { + unsafe { + xous::disconnect(self.conn).unwrap(); + } + } + } +} diff --git a/services/xous-swapper/src/main.rs b/services/xous-swapper/src/main.rs index dc6d9dd5e..0e6c92f08 100644 --- a/services/xous-swapper/src/main.rs +++ b/services/xous-swapper/src/main.rs @@ -58,7 +58,8 @@ use debug::*; use loader::swap::{SwapAlloc, SwapSpec, SWAP_CFG_VADDR, SWAP_COUNT_VADDR, SWAP_PT_VADDR, SWAP_RPT_VADDR}; use num_traits::*; use platform::{SwapHal, PAGE_SIZE}; -use xous::{MemoryFlags, MemoryRange, Result, CID, PID, SID}; +use xous::{MemoryFlags, MemoryRange, Result, PID}; +use xous_swapper::Opcode; /// Target of pages to free in case of a Hard OOM. Note that the PAGE_TARGET numbers /// are imprecise, in that there is a chance that one target is active during another @@ -118,16 +119,6 @@ pub enum KernelOp { HardOom = 3, } -/// public userspace & swapper handler -> swapper userspace ABI -#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive)] -#[repr(usize)] -pub enum Opcode { - /// Test messages - #[cfg(feature = "swap-userspace-testing")] - Test0, - None, -} - pub struct PtPage { pub entries: [u32; 1024], } @@ -217,20 +208,18 @@ impl SwapperSharedState { } struct SharedStateStorage { pub inner: Option, - pub _conn: CID, } impl SharedStateStorage { - pub fn init(&mut self, sid: SID) { + pub fn init(&mut self) { // Register the swapper with the kernel. Written as a raw syscall, since this is // the only instance of its use (no point in use-once code to wrap it). // This is an "early registration" which allows us to see debug output quickly, // even before we can constitute all of our shared state - let (s0, s1, s2, s3) = sid.to_u32(); xous::rsyscall(xous::SysCall::RegisterSwapper( - s0, - s1, - s2, - s3, + 0, + 0, + 0, + 0, swap_handler as *mut usize as usize, self as *mut SharedStateStorage as usize, )) @@ -653,10 +642,8 @@ fn swap_handler( } fn main() { - let sid = xous::create_server().unwrap(); - let conn = xous::connect(sid).unwrap(); - let mut sss = Box::new(SharedStateStorage { _conn: conn, inner: None }); - sss.init(sid); + let mut sss = Box::new(SharedStateStorage { inner: None }); + sss.init(); // init the log, but this is mostly unused. log_server::init_wait().unwrap(); @@ -705,6 +692,7 @@ fn main() { }); // This thread pings the free memory level and will try to clear memory to avoid OOM + // This claims sss, and is mutually exclusive with other options that claim sss #[cfg(feature = "oom-doom")] std::thread::spawn({ move || { @@ -720,6 +708,9 @@ fn main() { } }); + let xns = xous_api_names::XousNames::new().unwrap(); + let sid = xns.register_name(xous_swapper::SWAPPER_PUBLIC_NAME, None).unwrap(); + let mut msg_opt = None; loop { xous::reply_and_receive_next(sid, &mut msg_opt).unwrap(); @@ -727,6 +718,25 @@ fn main() { let op: Option = FromPrimitive::from_usize(msg.body.id()); log::debug!("Swapper got {:x?}", op); match op { + Some(Opcode::GarbageCollect) => { + if let Some(scalar) = msg.body.scalar_message_mut() { + let pages = scalar.arg1; + if pages > HARD_OOM_PAGE_TARGET * 2 { + log::warn!( + "Not honoring excessive GC request, reducing to {} pages", + HARD_OOM_PAGE_TARGET * 2 + ); + } + sss.inner.as_mut().unwrap().pages_to_free = pages.max(HARD_OOM_PAGE_TARGET * 2); + xous::rsyscall(xous::SysCall::SwapOp(SwapAbi::ClearMemoryNow as usize, 0, 0, 0, 0, 0, 0)) + .expect("ClearMemoryNow syscall failed"); + sss.inner.as_mut().unwrap().pages_to_free = HARD_OOM_PAGE_TARGET; + let free_pages = get_free_pages(); + log::info!("Free pages after GC: {}", free_pages); + // return the current free page count + scalar.arg1 = free_pages; + } + } #[cfg(feature = "swap-userspace-testing")] Some(Opcode::Test0) => { log::info!("Free mem: {}kiB", get_free_pages() * PAGE_SIZE / 1024);