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. |
||
|---|---|---|
| .. | ||
| bin | ||
| src | ||
| test | ||
| assemble.ps1 | ||
| assemble.sh | ||
| build.rs | ||
| Cargo.toml | ||
| link.x | ||
| README.md | ||
Xous Kernel
This contains the core kernel for Xous. It requires a stage 1 loader in order to start up, as it assumes the system is already running in Supervisor mode.
Building
To build the kernel, you will need a riscv32 target for Rust. Possible
targets include riscv32i-unknown-none-elf, riscv32imac-unknown-none-elf,
or riscv32imac-unknown-xous-elf.
- Decide what target you want. For simple, embedded systems this could
be
riscv32i-unknown-none-elf, and for more complex systems with compressed instructions you could useriscv32imac-unknown-none-elf. - Get Rust. Go to https://rustup.rs/ and follow its instructions.
- Install the proper toolchain:
rustup target add ${target_arch} - Build the kernel:
cargo build --release --target ${target_arch}
Using
To use the kernel, you must package it up into an arguments binary with
xous-tools.
Testing
TBD
Contribution Guidelines
Please see CONTRIBUTING for details on how to make a contribution.
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide its terms.
License
Copyright © 2020
This project is licensed under the Apache License 2.0 LICENSE. For accurate information, please check individual files.