MMotor OS
System

Memory

Motor OS uses 4 KiB pages, 2 MiB pages for large heaps, eager mappings by default, and guarded lazy stacks. Instead of an OOM killer it has admission control: a memory-growing operation is refused before it starts if it would push the machine below a safety floor, and a shared "memory pressure" flag lets sys-io shed load before it runs out.

Pages and mappings

Physical memory

The kernel manages RAM as 2 MiB blocks. A block is whole, which means free or serving one 2 MiB page, or split into 4 KiB pages. A split block keeps a free list threaded through its free pages, and every link carries a check word, so a corrupted or forged link is detected rather than followed. Memory below 128 MiB only supplies small pages; above it a whole block serves either a huge page or, as a last resort, small pages. Small pages are taken from the bottom of memory up and huge pages from the top down, and a block whose pages have all been freed becomes whole again, so the two sizes stay apart. Each CPU keeps a cursor into the block it last allocated from. The design, the boot-time shaping of the pool, and the kernel's virtual layout are in docs/kernel-mm.md.

The allocator

Every process's heap, and the kernel's own heap, is frusa, a no-std allocator written for Motor OS. Requests up to 4 KiB are rounded to a power-of-two size class and served from 64-slot blocks, each tracked by one bitmap; larger requests map pages directly. A thread keeps one private block per class for its temporaries, freed objects wait on short per-CPU lists, and a pointer is located by a binary search over an address-sorted index, so no operation walks the heap and no lock is held across a request to the kernel. Memory goes back to the kernel on demand: the runtime returns idle pages every five seconds once more than 1 MiB is idle, or as soon as a page is idle while the memory-pressure flag is up. The kernel uses the same crate with a per-CPU cache in front of it. The design is in docs/frusa.md.

Why admission control

Allocating N pages of data also costs page descriptors, page tables, and occasionally a new slab for kernel bookkeeping, about 2–2.5% on top of the data in measurements. Before admission control the kernel's low-memory check counted only the requested data pages and was separate from the allocation, so concurrent requests could all pass it, and one unprivileged process could empty the physical page pool and panic the kernel. Admission control is a stability mechanism, not a fairness policy: it does not pick a victim, does not promise that every lazy page will get backing, and does not attribute shared kernel objects to processes.

Floors

Two compile-time low-water marks over free small pages divide memory into zones:

available above USER_FLOOR (256 pages, 1 MiB):
    any process may start a memory-growing operation

SYS_IO_FLOOR (128 pages, 512 KiB) < available <= USER_FLOOR:
    only system processes may (sys-io, sys-tty, sys-init, strobe)

available <= SYS_IO_FLOOR:
    nobody may; what is left is for kernel work already in flight

The reserve between the floors belongs to processes with the system or I/O-manager capability; the lower floor keeps its historical sys-io name. The floors are constants, not configuration: an operator must not be able to configure the machine back into physical exhaustion. If measurements ever show they are too small, the floors are raised rather than a new allocation class added.

How an operation is admitted

Every userspace operation that grows memory (eager, lazy, shared, and contiguous mappings, page faults on lazy memory, stacks, address-space, process, and thread creation, kernel objects, IPC) is charged a conservative worst case, and a reservation for that charge is published in a global counter before the operation runs:

charge  = data_pages + ceil((data_pages + descriptors) / 32) + 64
admit iff (available - reservations) - charge >= floor

The reservation is held until the operation completes, which double-counts pages the operation has already allocated; that can refuse a request slightly early but can never admit too much. Fixed charges: a process 256 pages, a thread 64, a kernel object or IPC 16. A remote operation (sys-io loading a program into a new process) is charged against the target process's class, so a privileged loader does not widen an ordinary process's allowance.

What a refusal means:

Admission costs nothing measurable: in a release A/B test, eager 16-page allocate-and-free and warm lazy faults were within noise of the pre-admission build, and boot time was unchanged.

Pressure mode

The floors alone cannot keep sys-io alive, because sys-io's heap grows through the same allocator and a failed allocation aborts the process. Pressure mode stops sys-io's demand before the kernel has to refuse it. The kernel maintains a memory_pressure flag in a read-only page mapped into every process, with hysteresis so a single allocation or free cannot flip service on and off:

raise when free-for-admission <= 512 pages (2 MiB)
clear when free-for-admission >= 768 pages (3 MiB)

Any process can read the flag with one memory load. While it is up:

Nothing that must run during an episode may allocate; the refusal paths are allocation-free by construction. Established TCP connections keep working, because serving them allocates nothing. The flag clears from the page-free path as well, so it drops when the process that caused the pressure exits even if nobody else allocates.

Observability

Kernel metrics: mem.admission_refused_user, mem.admission_refused_sys_io, mem.admission_reserved_pages, mem.small_pages_low_water, mem.phys_small_pages_low_water, and the two floor gauges. sys-io metrics: net.pressure_active, net.pressure_entries, net.pressure_refused, net.pressure_refused_clients, net.pressure_deferred_replenish, fs.pressure_refused, and fs.pressure_refused_clients. A single syscall (F_QUERY_ADMISSION_STATS) reports availability, reservations, floors, and watermarks in one call. The block pool reports its own gauges, mem.blocks_* and mem.pages_*. See Logs and diagnostics for how to read metrics.

Deliberately not built

The full argument, including the measurements that validated the floors, is in docs/oom-handling.md; the kernel's physical and virtual memory management is in docs/kernel-mm.md and the allocator in docs/frusa.md.