From dfd82c94b841bab155a4ebb68c73256de20ec1e2 Mon Sep 17 00:00:00 2001 From: bunnie Date: Sun, 18 May 2025 04:23:53 +0800 Subject: [PATCH] refactor write page syscall for explicitly managed writing this is a much simpler, race condition-free version of flash writing which has the penalty of requiring a copy of the page to be made by the caller before the write. However the copy is mostly already paid for because on the initial access to the page, we had to copy the flash into RAM. --- kernel/src/swap.rs | 77 +++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/kernel/src/swap.rs b/kernel/src/swap.rs index d99e1e82b..eea91265c 100644 --- a/kernel/src/swap.rs +++ b/kernel/src/swap.rs @@ -3,10 +3,10 @@ use core::cmp::Ordering; -use loader::SWAP_FLG_DIRTY; use loader::SWAP_FLG_WIRED; use xous_kernel::SWAPPER_PID; use xous_kernel::arch::EXCEPTION_STACK_TOP; +use xous_kernel::arch::MMAP_VIRT_BASE; use xous_kernel::arch::PAGE_SIZE; use xous_kernel::arch::SWAP_RPT_VADDR; use xous_kernel::{PID, SysCallResult, TID}; @@ -40,8 +40,7 @@ pub enum SwapAbi { HardOom = 4, StealPage = 5, ReleaseMemory = 6, - MarkDirty = 7, - Sync = 8, + WritePage = 7, } /// SYNC WITH `xous-swapper/src/main.rs` impl SwapAbi { @@ -54,8 +53,7 @@ impl SwapAbi { 4 => HardOom, 5 => StealPage, 6 => ReleaseMemory, - 7 => MarkDirty, - 8 => Sync, + 7 => WritePage, _ => Invalid, } } @@ -71,7 +69,7 @@ pub enum BlockingSwapOp { ReadFromSwap(PID, TID, usize, usize, usize), /// PID of the target block, current paddr of block, original vaddr in the space of target block PID, /// physical address of block. Returns to PID of the target block. - WriteToFlash(PID, TID, usize, usize, usize), + WriteToFlash(PID, TID, PID, usize, usize), /// Immediate OOM. Drop everything and try to recover; from here until exit, everything runs in /// an un-interruptable context, no progress allowed. This currently can only originate from one /// location, if we need multi-location origin then we have to also track the re-entry point in the @@ -154,12 +152,6 @@ impl SwapAlloc { pub fn set_wired(&mut self) { self.vpn |= SWAP_FLG_WIRED; } - pub fn set_dirty(&mut self) { self.vpn |= SWAP_FLG_DIRTY; } - - pub fn clear_dirty(&mut self) { self.vpn &= !SWAP_FLG_DIRTY; } - - pub fn is_dirty(&self) -> bool { self.vpn & SWAP_FLG_DIRTY == SWAP_FLG_DIRTY } - #[cfg(feature = "debug-swap-verbose")] pub fn get_raw_vpn(&self) -> u32 { self.vpn } @@ -684,33 +676,30 @@ impl Swap { } } - /// This passes on to the userspace handler a page that needs to be written to SPI flash. - pub fn flush_page_syscall( + pub fn write_page_syscall( &mut self, - target_vaddr_in_pid: usize, - paddr: usize, - target_pid: PID, - target_tid: usize, + src_pid: PID, + flash_offset: usize, + page_vaddr_in_swapper: usize, ) -> ! { - let block_vaddr_in_swap = - crate::arch::mem::map_page_to_swapper(paddr).expect("couldn't map target page to swapper"); - // we are now in the swapper's memory space + let target_pid = crate::arch::process::current_pid(); + let target_tid = crate::arch::process::current_tid(); // #[cfg(feature = "debug-swap-verbose")] println!( - "flush_page - userspace activate from pid{:?}/tid{:?} for vaddr {:x?} -> paddr {:x?}", - target_pid, target_tid, target_vaddr_in_pid, paddr + "write_page - userspace activate for pid{:?}/tid{:?} for vaddr {:x?} -> offset {:x?}", + target_pid, target_tid, page_vaddr_in_swapper, flash_offset ); // prevent context switching to avoid re-entrant calls while handling a call self.swap_stop_irq(); - // this is safe because map_page_to_swapper() leaves us in the swapper memory context + // this is safe because the syscall pre-amble checks that we're in the swapper context unsafe { self.blocking_activate_swapper(BlockingSwapOp::WriteToFlash( target_pid, target_tid, - target_vaddr_in_pid, - block_vaddr_in_swap, - paddr, + src_pid, + page_vaddr_in_swapper, + flash_offset, )); } } @@ -730,12 +719,11 @@ impl Swap { self.swapper_args[3] = vaddr_in_pid; self.swapper_args[4] = vaddr_in_swap; } - BlockingSwapOp::WriteToFlash(pid, _tid, vaddr_in_pid, vaddr_in_swap, _paddr) => { + BlockingSwapOp::WriteToFlash(_pid, _tid, _src_pid, vaddr_in_swap, flash_offset) => { self.swapper_args[0] = self.swapper_state; self.swapper_args[1] = 4; // WriteToFlash - self.swapper_args[2] = pid.get() as usize; - self.swapper_args[3] = vaddr_in_pid; - self.swapper_args[4] = vaddr_in_swap; + self.swapper_args[2] = vaddr_in_swap; + self.swapper_args[3] = flash_offset; } BlockingSwapOp::HardOomSyscall(_tid, _pid) => { self.swapper_args[0] = self.swapper_state; @@ -805,18 +793,29 @@ impl Swap { } // Called from any process. Clear the dirty bit on the RPT when exiting. No pages are unmapped // by this routine, that would be handled by the OOMer, if at all. - Some(BlockingSwapOp::WriteToFlash(pid, tid, _vaddr_in_pid, vaddr_in_swap, paddr)) => { + Some(BlockingSwapOp::WriteToFlash(pid, tid, src_pid, _vpage_addr_in_swapper, flash_offset)) => { + // this works because the V:P mapping for the flash memory is 1:1 for the LSBs + let flash_vaddr = MMAP_VIRT_BASE + flash_offset; + // evict_page_inner marks the page as swapped/invalid in src_pid, but also maps the page + // into the swapper's address space + let swap_vaddr = crate::arch::mem::evict_page_inner(src_pid, flash_vaddr).unwrap(); + + // release the page from the swapper's address space MemoryManager::with_mut(|mm| { - // we are in the swapper's memory space a this point - // unmap the page from the swapper - crate::arch::mem::unmap_page_inner(mm, vaddr_in_swap) - .expect("couldn't unmap page lent to swapper"); - // the page map into the target space happens after the syscall returns + let paddr = crate::arch::mem::virt_to_phys(swap_vaddr).unwrap() as usize; + // #[cfg(feature = "debug-swap-verbose")] + println!("Release flash backing page - paddr {:x}", paddr); + // this call unmaps the virtual page from the page table + crate::arch::mem::unmap_page_inner(mm, swap_vaddr).expect("couldn't unmap page"); + // This call releases the physical page from the RPT - the pid has to match that of + // the original owner. This is the "pointy end" of the stick; + // after this call, the memory is now back into the free pool. + mm.release_page_swap(paddr as *mut usize, src_pid) + .expect("couldn't free page that was swapped out"); }); - // mark the physical page as clean - MemoryManager::with_mut(|mm| mm.mark_rpt_clean(paddr).expect("couldn't mark RPT as clean")); // Unhalt IRQs + println!("WriteToFlash exit - restore IRQ"); self.swap_restore_irq(); (pid, tid) }