mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-04 04:27:34 +00:00
A particular edge case was finally reproduced & fixed. The edge case occurs when a swap is triggered while handling an interrupt, and an OOM happens during the context switch to/from the interrupt. The background on this is that the hardware drivers for the swapper, as well as much of the logical heavy lifting on e.g. what pages to swap out, live in a userspace process known as xous-swapper. The userspace process has to run free of interrupts - when you're OOM'd or trying to swap, interrupts should not happen. The way to do this in Xous is to run that code inside an interurpt context, as interrupts are not interruptable. Thus, the xous swapper userspace consists of two halves: a regular userspace stub that provides some amount of control/debug over the swapper, and a handler that gets run in a "pseudo-interrupt" context. Like all other interrupts, the handler "borrows" the stack of the currently running thread - interrupt contexts don't have an explicit stack, they just grow on top of the current stack of the current thread in the current process at the time of the interrupt. This is broken because when you have a swap event inside an interrupt context, there are conditions where you might not have a stack. What was in fact happening is that the stack pointer was being orphaned as being the "exception stack" - which is a temporary location, only accessible by the kernel, used for storing all the registers going into and out of exceptions. Since no stack pointer is set up, the moment the userspace starts to run and spill its registers to stack, the system trips a "nested swap" event, which leads to a panic because this is not allowed. The fix to this is to devote two hard-wired pages, mapped in the loader, to be the swapper's private stack area. Instead of behaving like an interrupt and borrowing the caller's stack, the swapper instead goes back to its hard-wired stack to do all the processing. This should also improve the performance of swap handling in some cases because we can now guarantee the stack is always in-memory and doesn't need to be swapped in during swap processing. One complication is that the "reserved memory" that the swapper puts aside in case it *does* need to allocate extra pages during handling can't be freed at the top of the OOM routine. Thus if the swapper ends up using new pages, we should expect yet another crash. However, in practice, this doesn't seem to happen, because the cases in which we hit needing a new page are typically related to cases early on in boot where superpages/new page table entries are being allocated, or if we're growing the heap that passes data back and forth between swap and userspace. Because this call triggers in such a specific condition, I hypothesize that actually in practice it should generally be able to run without allocations. One case where maybe it could hit an allocation is if the swapper hit, for the first time, in an interrupt handler that was swapped out, and never hit before, and the interrupt handler needed a superpage to be allocated, thus triggering an allocation request for a new PTE or two. I think this is probably pretty unlikely, but if we do hit it, the fix would be to, for the purposes of targets with "swap" enabled, to mark unmap_memory and map_memory to be legal syscalls in interrupt contexts. These are normally illegal because interrupts should always return - and these calls could potentially fail or block. However, I think in this case it's a trade-off between "we should be failing if we run out of memory" and that failure being a potentially incorrect behavior that is guarded against by a runtime check to discourage such calls. |
||
|---|---|---|
| .. | ||
| arch | ||
| debug | ||
| platform | ||
| args.rs | ||
| io.rs | ||
| irq.rs | ||
| macros.rs | ||
| main.rs | ||
| mem.rs | ||
| server.rs | ||
| services.rs | ||
| swap.rs | ||
| syscall.rs | ||
| test.rs | ||
| utils.rs | ||