607 lines · plain
1.. SPDX-License-Identifier: GPL-2.02 3================================================================================4arch/wasm32 mm model (K3 spec)5================================================================================6 7:Status: K3 (real ``mm_init`` / page allocator / slub / wasm atomics).8 Stable through K6; revisions require a new section + §15 row.9 10This document is the source of truth for *how the wasm32 port models11physical memory, page sizing, vmalloc, atomics, and per-CPU areas*. It12plays the same role for the memory subsystem that ``memory-model.rst``13plays for the cross-module memory ABI and ``boot-protocol.rst`` plays14for the boot-time handoff: spec-first, code-second. Treat any15divergence between this document and ``arch/wasm32/include/asm/page.h``16/ ``arch/wasm32/include/asm/atomic.h`` / ``arch/wasm32/include/asm/cmpxchg.h``17as a bug in the code.18 19--------------------------------------------------------------------------------200. K3-phase scope at a glance21--------------------------------------------------------------------------------22 23(a) **New cross-module ABI introduced by K3.** Two surfaces.24 25 - **kernel C ↔ wasm runtime (atomics).** The K2-era atomic_t /26 refcount_t / cmpxchg call sites were lowered to plain non-atomic27 loads and stores via ``asm-generic/atomic.h``. K3 replaces that28 with real wasm-threads atomic opcodes (``i32.atomic.rmw.cmpxchg``,29 ``i32.atomic.rmw.{add,sub,and,or,xor,xchg}``, ``i64.atomic.*``, the30 fence / load / store family) backed by clang ``__atomic_*``31 builtins compiled with ``-matomics -mbulk-memory``. This is a32 silent-ABI-change in the sense that the kernel's *behaviour* under33 concurrent access becomes correct; no name or signature changes,34 so no version bump is needed in any of the named cross-module35 ABIs. §3 below names the toolchain-pinned encoding rules so a36 future toolchain update can be checked against this spec.37 38 - **kernel C ↔ HardwareJS (page sizing).** ``PAGE_SIZE = 4096`` (see39 Q1 in §2). This affects the eventual userspace ABI: ``mmap()``40 alignment, ``getpagesize()``, ``sysconf(_SC_PAGESIZE)``, ELF41 program-header alignment for ``binfmt_wasm`` (K-phase U1+) all42 return / require 4096-byte alignment. HardwareJS never has to43 *know* the kernel's PAGE_SIZE — it serves env.kernel_memory as a44 flat SAB and the kernel chops it into 4 KiB pages internally — but45 any HardwareJS code that *interprets* user-memory mapping requests46 (e.g. the future ``linux.mmap_user_page`` host primitive at K4+)47 will see 4096-byte chunks at 4096-aligned offsets. This is48 documented here so K4+ doesn't relitigate it.49 50(b) **Architectural decisions K3 forces.** Q1–Q5 in §2 below. None51 are technically reversible without an ABI version bump.52 53(c) **§15 debt this phase predictably opens.** Predicted at K3 open,54 refined at K3 close as the implementation surfaced two additional55 rows that were not visible from the spec alone. Final K3-close56 debt list (all six rows landed in docs/ARCHITECTURE.md §15):57 58 1. **vmalloc is contiguous-backed.** If a future subsystem actually59 needs ``vmalloc()`` to return *virtually*-contiguous /60 *physically*-discontiguous memory, this is the design61 constraint to revisit. See §2 Q2 and §6 below.62 2. **Multi-memory atomic textual syntax is LLVM-blocked.** Our63 ``arch/wasm32/include/asm/cmpxchg-user.h`` (introduced for K464 futexes) MUST use raw ``.byte`` inline asm to emit the65 multi-memory form of atomic ops. Filed as LLVM-side debt with66 pointer to the spec-side text describing the encoding. When67 LLVM upstream adds the operand, we delete the ``.byte``68 wrappers — see §3.2.69 3. **NR_CPUS = 8 wastes percpu memory at K3.** Slub allocates70 per-CPU caches for 8 CPUs; only CPU 0 is online. ~7× memory71 waste, ~700 KiB of slub bookkeeping. Justified because K472 "CPUs coming online" then doesn't have to resize allocations.73 Re-tunable at v0.1.0+ if browser concurrency reality differs74 from the K4 plan.75 4. **Linker-script-anchor symbols for percpu and sched_class76 ordering** are stubbed via ``arch/wasm32/kernel/percpu_anchors.c``77 using 1-byte char-array placeholders in synthetic sections78 plus ``--no-merge-data-segments`` at link time. The kernel's79 ``include/asm-generic/vmlinux.lds.h::PERCPU_INPUT`` and80 ``SCHED_DATA`` macros enumerate the segment ordering wasm-ld81 does not consume from the linker script. Discovered during K382 implementation. Owed real fix at K5/K6 via a post-link Python83 pass (sibling to ``wasm-add-memory.py``) that walks data84 segments and emits ``--defsym=__per_cpu_start=...``85 ``--defsym=__per_cpu_end=...`` /86 ``--defsym=__{stop,dl,rt,fair,idle}_sched_class=...`` before87 final link. See §7 below.88 5. **CONFIG_SLUB_TINY=y as a K3 workaround.** Discovered K389 closing: with per-CPU SLUB freelists enabled90 (``!CONFIG_SLUB_TINY``), kmalloc-256 allocations return91 misaligned pointers under sustained traffic — traced to92 ``static_size``-driven offset miscomputation in the per-CPU93 dynamic allocator (the per-CPU anchor row above doesn't94 express the kernel's real percpu image size). SLUB_TINY95 removes the per-CPU fastpath entirely; allocations go via96 the per-node partial list and are stable. K3 acceptance97 test confirms kmalloc/kfree work under concurrent SAB98 pressure. Re-flippable to ``CONFIG_SLUB_TINY=n`` once the99 post-link percpu pass lands at K5/K6.100 6. **30-second test watchdog** in the K3 vitest happy-path101 because the kernel hangs in ``rest_init`` / kthreadd setup102 rather than panicking — exactly the K3 acceptance shape, but103 also a hint that K4's scheduler work has a specific entry104 point. Recorded so K4 doesn't accidentally relitigate.105 106--------------------------------------------------------------------------------1071. Physical memory model108--------------------------------------------------------------------------------109 110In this port, "physical memory" is the bytes inside ``env.kernel_memory``111— a single SharedArrayBuffer-backed ``WebAssembly.Memory`` allocated112by HardwareJS at boot. The kernel sees a flat physical address space113``[0, total_memory)``. There is no MMIO, no DMA, no NUMA. There is no114``ZONE_HIGHMEM`` (no MM aperture distinction in wasm), no115``ZONE_DMA`` / ``ZONE_DMA32`` (no DMA), no ``ZONE_DEVICE`` (no PMEM /116HMM). The only zone is ``ZONE_NORMAL``.117 118The mapping between physical addresses (``phys_addr_t``) and kernel119virtual addresses (``unsigned long``) is the identity — there is no120``PAGE_OFFSET`` translation. ``__pa(x) == x``, ``__va(x) == x``. This121is the simplest possible flat model and matches NOMMU arches like122ARC's hsdk-noplv1 configuration; the kernel's flat-mode macros in123``include/asm-generic/`` Just Work.124 125Memblock is seeded at K2 from ``boot_info.usable_{start,end}`` (see126``Documentation/wasm/boot-protocol.rst``). The kernel image is127``memblock_reserve``-d from offset 0 through ``__heap_base``, so the128buddy allocator never reclaims kernel ``.text`` / ``.rodata`` /129``.data`` / ``.bss`` pages. memblock hands its remaining regions to130the buddy allocator during ``mm_init``.131 132--------------------------------------------------------------------------------1332. Architectural decisions Q1–Q5 (the K3 opening five)134--------------------------------------------------------------------------------135 136Q1. PAGE_SIZE = 4096137~~~~~~~~~~~~~~~~~~~~138 139**Decision: PAGE_SIZE = 4096, PAGE_SHIFT = 12.**140 141Considered:142 143* **4 KiB.** Matches x86, matches every Linux userspace ABI144 expectation, matches musl / glibc / POSIX defaults for145 ``sysconf(_SC_PAGESIZE)``. Finer-grained metadata: every 4 KiB146 region gets its own ``struct page``, which is `~8 % memory147 overhead on a 64 MiB heap (sizeof(struct page) ≈ 64 bytes, 16384148 pages = 1 MiB of ``mem_map``). Forces PAGE_SIZE / wasm-page149 conversion arithmetic at the memblock / page-allocator boundary150 (1 wasm-page = 16 kernel pages), but this is a one-time151 shift-by-4 at every allocator hand-off and is not on any hot path.152 153* **65536.** Matches wasm's own ``memory.grow`` page size. No154 PAGE_SIZE-vs-wasm-page conversion needed at the allocator155 boundary. Half the ``mem_map`` overhead (~0.5 MiB on a 64 MiB156 heap). BUT: every userspace binary built against musl / glibc157 expects 4 KiB pages — ``mmap(NULL, 4096, ...)`` would have to158 silently round up to 64 KiB, which violates ``POSIX_MAP_FIXED``159 semantics; ``sysconf(_SC_PAGESIZE)`` would return 65536, which160 most userspace handles correctly but some hard-coded161 ``#define PAGE_SIZE 4096`` in third-party code does not; ELF162 program-header alignment for binfmt_wasm (U1+) would be 64 KiB163 rather than 4 KiB, which is fine but unusual and may confuse164 cross-built ELF inspection tools. The userspace ABI cost is165 irrecoverable: this is a kernel ↔ userspace ABI decision that166 cannot be changed after v0.1.0.167 168**Rationale for 4096.** Userspace ABI expectations are more169constraining than internal alignment with ``memory.grow``. The170conversion arithmetic is mechanical and one-time. ``mem_map``171overhead at 8 % of a small heap is acceptable; on a 1 GiB heap172(the wasm32 ceiling) it is ~16 MiB which is comfortable. K7's173rootfs and U2's bash/coreutils will both run unmodified upstream174binaries built for 4 KiB-page hosts; choosing 65536 here would175force at minimum a sed-pass over upstream package configure176scripts that introspect PAGE_SIZE, which is the exact category of177"fork a file outside arch/wasm32/" that §16's PR rejection178criterion forbids.179 180Q2. vmalloc semantics181~~~~~~~~~~~~~~~~~~~~~182 183**Decision: vmalloc is contiguous-backed by the buddy allocator184with a hard upper bound of ``MAX_ORDER * PAGE_SIZE`` (== 4 MiB at185PAGE_SIZE=4096, MAX_ORDER=10). Real virtually-contiguous /186physically-discontiguous mappings are not supported.**187 188Considered:189 190* **(i) Contiguous-backed.** ``vmalloc(N)`` becomes191 ``__alloc_pages(get_order(N))`` returning the kernel-virtual192 (== physical) address of the contiguous run. ``vfree`` calls193 ``free_pages`` on the same range. Most kernel users of194 ``vmalloc`` want "large allocation that doesn't fail if the195 *physical* heap is fragmented" — they don't care that the196 mapping is virtually contiguous. On our model, fragmentation197 of ``env.kernel_memory`` is the *only* fragmentation, so the198 "physically discontiguous" property has no meaning to begin199 with: any contiguous run we can give them IS the answer they200 want.201 202* **(ii) Disable vmalloc entirely** by removing203 ``CONFIG_HAVE_VMALLOC`` (or equivalent) and forcing every204 caller to use ``alloc_pages_node`` / ``alloc_pages_exact``.205 This is the technically cleanest answer but breaks a huge206 number of upstream-Linux callers that just want207 ``vmalloc(N)`` to "give me N bytes of contiguous-ish kernel208 memory."209 210* **(iii) Pretend to vmalloc by setting up a separate kernel211 virtual address range** that the buddy allocator's212 ``alloc_pages`` calls populate via wasm's ``memory.grow``.213 Discarded: wasm has no page tables, ``memory.grow`` adds214 pages at the *end* of the linear memory not at arbitrary215 virtual addresses, and the kernel cannot create the216 "discontiguous mapping" illusion without a page table or217 equivalent address translation.218 219**Rationale for (i).** Most callers don't need the220discontiguous property; the hard upper bound from221``MAX_ORDER`` is the same upper bound a real ``vmalloc`` would222hit on a fragmented heap anyway (just from a different cause).223Subsystems that genuinely need >4 MiB single allocations are224the same subsystems that would have failed on a real arch with225a moderately-fragmented heap, so they already have fallback226strategies; we're not adding a new failure mode, we're227exposing a slightly more predictable version of an existing228one.229 230**§15 debt row to add at K3 close.** "vmalloc-contiguous-231backed; if a future subsystem actually needs discontiguous232mapping, this is the design constraint to revisit (would233require either a pretend-pgtable in arch/wasm32 or a234``linux.kernel_vmap`` host primitive that asks HardwareJS to235maintain a paddr→vaddr remap table)."236 237Q3. Cross-memory atomics from the kernel against user memory238~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~239 240**Decision: cross-memory atomic ops emit the wasm-threads ×241multi-memory binary form via raw ``.byte`` inline asm242wrappers (similar to how arch/wasm32/lib/uaccess.S wraps243multi-memory ``memory.copy``). LLVM 22's textual wasm244assembler does NOT yet accept the memidx operand on atomic245ops, but the runtime DOES accept the emitted bytes.246EMPIRICALLY VERIFIED on the K3 opening commit's pinned247toolchain (see evidence below). All cross-memory atomic248ops live in ``arch/wasm32/include/asm/cmpxchg-user.h`` and249``arch/wasm32/lib/atomics-user.S`` (introduced for K4250futexes; declared here so the K3 plan can encode the251constraint).**252 253Evidence captured at K3 opening (write-up of the empirical254probe in ``linux-wasm/tools/k3-q3-probe/`` for255reproducibility):256 257Pinned toolchain:258 259* ``clang version 22.0.0git`` (LLVM-project commit260 ``60513b8d6ebacde46e8fbe4faf1319ac87e990e3``)261* ``LLD 22.0.0`` (same commit)262* Node ``v25.2.0`` (V8)263 264Probe encoding (memidx = 1, align = 2, offset = 0)::265 266 fe 48 42 01 00267 │ │ │ │ └── offset (ULEB128 = 0)268 │ │ │ └───── memidx (ULEB128 = 1, = env.user_memory)269 │ │ └──────── align byte with bit 6 set270 │ │ (0x40 | 0x02 = 0x42 → "memidx follows, align=2")271 │ └─────────── opcode (i32.atomic.rmw.cmpxchg)272 └────────────── atomic-opcode prefix273 274Observed runtime behaviour: cross-memory cmpxchg against275``env.user_memory`` offset 0 with expected=0 desired=99276returned 0 (the old value) AND ``user_memory[0]`` is 99277after the call. The cross-memory atomic executed278**correctly** under V8.279 280``llvm-objdump`` mis-disassembles the bytes (it parses the281``0x42`` as a normal align byte, reads the next ``0x01`` as282the offset, then mis-decodes the trailing ``0x00`` as283``unreachable`` instead of a continuation of the memarg).284That's a disassembler bug, not a binary bug. The285encoded-and-instantiated wasm executes as the spec286specifies.287 288**§15 debt row to add at K3 close.** "Multi-memory atomic289textual syntax is LLVM-blocked. Our cross-memory atomic290wrappers use raw ``.byte`` inline asm; replace with291textual operand when upstream LLVM lands the syntax. Owed:292toolchain-upstream-bump (not K-phase tied)."293 294Q4. CONFIG_NR_CPUS for K3295~~~~~~~~~~~~~~~~~~~~~~~~~296 297**Decision: ``CONFIG_NR_CPUS=8``, ``CONFIG_SMP=y``,298``num_online_cpus() == 1`` for all of K3.**299 300Considered:301 302* **NR_CPUS=1, CONFIG_SMP=n.** Simplifies K3: no per-CPU303 caches, no IPI machinery, no smp_processor_id rerouting.304 But forces K4 to redo slub allocations when CPUs come305 online. slub's per-CPU caches are allocated at306 ``kmem_cache_create`` time, indexed by ``smp_processor_id``;307 resizing them after-the-fact is non-trivial (slub does308 not currently support dynamic NR_CPUS change). K4 would309 have to either re-init the slab allocator (risky) or310 pre-allocate but not use the extra slots (which is what311 NR_CPUS=8 already does).312 313* **NR_CPUS=8, CONFIG_SMP=y.** Wastes ~7× per-CPU memory at314 K3 (one Worker, 7 unused slots × per-CPU caches × every315 kmem_cache ≈ 700 KiB total slub-bookkeeping on a stock316 6.12 config). K4 just flips Workers to online via317 ``set_cpu_online(cpu, true)`` without resizing any318 allocation. The slub per-CPU caches that K3 created are319 immediately usable by K4 CPUs.320 321* **NR_CPUS=hardwareConcurrency.** Tempting (matches322 browser reality), but ``CONFIG_NR_CPUS`` is compile-time;323 we'd have to recompile per-browser at boot, which is the324 exact bad-engineering pattern. Browsers report 4–8325 cores in 95% of cases; picking 8 (a fixed ceiling) is the326 conservative choice. Beyond 8, K4+ can either spawn more327 Workers via virtual SMP (Workers > CPUs, scheduling at328 the JS engine layer) or bump the constant.329 330* **NR_CPUS=64.** The standard "many-CPU" pick on real331 servers; gross overkill in a browser. Would waste ~63×332 per-CPU memory for no gain.333 334**Rationale for 8.** Forward-compat with K4 without335re-allocating; matches typical browser hardware concurrency;336``CONFIG_NR_CPUS`` is a compile-time constant in mainline337Linux and 8 is a small enough number to keep the338"NR_CPUS-dependent allocation" footprint bounded. Recorded339in ``arch/wasm32/configs/wasm32_defconfig`` so the next340``make configure`` picks it up.341 342Q5. The K0 page-table model343~~~~~~~~~~~~~~~~~~~~~~~~~~~344 345**Decision: the K0 ``nopud + nopmd + degenerate single-level346PTE used as VMA bookkeeping`` model still holds for K3. No347real PTE allocation is triggered by ``mm_init`` /348``kmem_cache_init`` / ``kmem_cache_init_late`` on our349NOMMU-style flat model.**350 351Verification path:352 353* ``mm_init`` calls (in order, ignoring x86 / s390 -isms):354 ``set_max_mapnr``, ``init_mem_size_check``, ``mem_init``355 (zone walkers, no page-table use), ``kvfree_rcu_init``356 (slab cache, no page-table use), ``kmem_cache_init``357 (slab init, allocates ``kmem_cache_node`` and358 ``kmem_cache`` slabs — no page-table use),359 ``kmem_cache_init_late`` (creates the kmalloc caches,360 ``kfree`` works after — no page-table use), ``vmalloc_init``361 (initialises the vmalloc tree structures; on our model362 this resolves to "buddy-backed contiguous" per Q2 above,363 no page-table use), ``pgtable_init`` (creates the364 ptlock_caches slab IFF ``CONFIG_SPLIT_PTE_PTLOCKS`` is on365 — which we explicitly turn off in our defconfig, so this366 is a no-op call).367 368* ``pte_t`` is laid out per ``include/asm-generic/pgtable-nopud.h``369 + ``pgtable-nopmd.h`` + the wasm32 ``arch/wasm32/include/370 asm/pgtable.h``. ``pte_t`` is one ``unsigned long`` whose371 high bits are VMA flags and whose low bits would be the372 PFN on a real arch (but are unused on us since373 ``__pa == __va``). No pte allocation chain exercised at K3.374 375* ``mem_map[]`` IS allocated by memblock during376 ``mm_init`` (specifically inside ``free_area_init`` →377 ``alloc_node_mem_map``). With PAGE_SIZE=4096 and a 64 MiB378 test heap, that's 16384 ``struct page`` entries × ~64379 bytes = 1 MiB of bookkeeping, sized at ``mem_map`` init380 time and never re-grown.381 382**Conclusion: model unchanged for K3.** The first K-phase383that DOES touch pte allocation is K4 (when ``do_fork`` /384``copy_mm`` runs against a new ``mm_struct``); K7 (when385``mmap`` is exposed to userspace via binfmt_wasm) is when386the degenerate PTE-as-VMA-bookkeeping property gets387exercised end-to-end. We revisit then if anything surprises.388 389--------------------------------------------------------------------------------3903. Atomics implementation391--------------------------------------------------------------------------------392 3933.1 Same-memory atomics (kernel-internal)394~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~395 396``arch/wasm32/include/asm/atomic.h`` and ``arch/wasm32/include/397asm/cmpxchg.h`` wrap clang's ``__atomic_*`` / ``__c11_atomic_*``398builtins, with explicit memory-order arguments. The kernel's399``atomic_t``, ``atomic_long_t``, ``refcount_t``, RCU pointer400swaps, ``cmpxchg``, ``xchg``, ``set_bit`` / ``clear_bit`` /401``test_and_set_bit``, and the wait/wake futex word402manipulations all go through these wrappers. The Kbuild flag403``-matomics -mbulk-memory`` causes clang to lower the builtins404to wasm-threads atomic opcodes against the default memory405(== ``env.kernel_memory`` in the kernel module's view).406 407Memory-order mapping (Linux ↔ wasm):408 409============ ===================== ================================410Linux clang builtin order wasm opcode emitted411============ ===================== ================================412relaxed __ATOMIC_RELAXED atomic op (acquire-release-free)413acquire __ATOMIC_ACQUIRE atomic op + fence414release __ATOMIC_RELEASE atomic op + fence415acq_rel __ATOMIC_ACQ_REL atomic op + 2 fences416seq_cst __ATOMIC_SEQ_CST atomic op + 2 fences417============ ===================== ================================418 419The wasm-threads proposal defines all atomic ops to be420sequentially consistent; the fence machinery exists for code421generators that want the Linux-style fine-grained ordering.422We honour the Linux memory-order argument to maximise the423chance that a relaxed atomic stays a single opcode rather424than acquiring fence pollution.425 4263.2 Cross-memory atomics (K4 futex preview)427~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~428 429K4 needs ``cmpxchg`` (and ``xchg``, ``add``) against430``env.user_memory`` from the kernel module's code — for futex431word manipulation: the futex word lives in user memory; the432kernel module manipulates it atomically when ``schedule()``433sleeps a task or a wake_up_process delivers a notify.434 435Per Q3's empirical probe, V8 accepts the multi-memory atomic436binary encoding but LLVM 22 does not accept it in textual437form. The K4 implementation lives in438``arch/wasm32/include/asm/cmpxchg-user.h`` (header for the439C-side API: ``cmpxchg_user(ptr_in_user, old, new)``) and440``arch/wasm32/lib/atomics-user.S`` (the inline-asm or441raw-byte function bodies). Mirrors the442``arch/wasm32/lib/uaccess.S`` pattern of "C wraps a tiny443.S that wraps the wasm opcode we can't yet name in textual444syntax."445 4463.3 wasm-inspect atomic-opcode count447~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~448 449Every K-phase's close-commit body includes a line of the form::450 451 wasm-inspect atomic-opcode counts (vmlinux.wasm):452 i32.atomic.rmw.cmpxchg N453 i32.atomic.rmw.add N454 ...455 i32.atomic.store N456 i32.atomic.load N457 memory.atomic.wait32 N458 memory.atomic.notify N459 460K2 baseline (pre-K3) was: 1 ``i32.atomic.rmw.cmpxchg`` (in461``__wasm_init_memory``), 1 ``i32.atomic.store``, 1462``memory.atomic.wait32``, 1 ``memory.atomic.notify``. All463were wasm-ld-injected or host-import-shim, **not** kernel C464code. K3 acceptance requires the cmpxchg count to be465≫ 1 specifically because kernel C code lowered through real466atomics.467 468--------------------------------------------------------------------------------4694. Buddy allocator470--------------------------------------------------------------------------------471 472``mm/page_alloc.c`` runs unmodified. ``mem_map[]`` is sized by473memblock to ``(usable_end - usable_start) / PAGE_SIZE`` page474descriptors. ``free_area_init`` populates a single475``ZONE_NORMAL`` zone. ``MAX_ORDER`` (the maximum buddy order)476stays at the upstream default of 10 — 4 MiB max single477allocation at PAGE_SIZE=4096; this also caps ``vmalloc`` per478Q2 above. A future ``CONFIG_FORCE_MAX_ZONEORDER`` bump can479raise it if a subsystem demands it; the present default480matches stock x86_64 and is the most-tested code path.481 482--------------------------------------------------------------------------------4835. Slub (SLUB allocator)484--------------------------------------------------------------------------------485 486K3 picks ``CONFIG_SLUB=y`` (slub is the upstream default;487slob is removed in 6.12; slab is being deprecated).488 489**K3 close: ``CONFIG_SLUB_TINY=y``.** Per the §0(c) debt row4905, K3 ships with ``CONFIG_SLUB_TINY=y``, which removes the491per-CPU SLUB fastpath. Allocations go through the per-node492partial list. This is a workaround: when ``CONFIG_SLUB_TINY=n``493plus our current per-CPU anchor setup, the per-CPU dynamic494allocator's ``static_size``-driven offset math hands out495``cpu_slab`` struct pointers that overlap addressable496adjacencies in the per-CPU dynamic area, corrupting497kmalloc-256's freelist pointer with the address of an adjacent498kmalloc-128 partial field, which then fails the alignment499``BUG_ON`` in ``kernel/workqueue.c::init_pwq``. Real fix is the500percpu post-link Python pass (§0(c) debt row 4); when it lands,501``CONFIG_SLUB_TINY=n`` becomes selectable again.502 503``kmem_cache_init`` follows the upstream choreography:504bootstrap a tiny static ``kmem_cache_node`` slab, then a505static ``kmem_cache`` slab, then promote both to dynamically-506allocated. ``kmem_cache_init_late`` creates the kmalloc507caches (sized 8, 16, 32, …, 8192, 16384) and sets the508``slab_state`` to ``UP``. After that ``kmalloc`` / ``kfree``509work.510 511Arch hooks required:512 513* None confirmed at K3 open; none added at K3 close. The514 early-boot ``kmem_cache_node`` bootstrap in ``mm/slub.c:5177``515 (which K2 panicked in) is what K3 unblocks by making516 memblock and the page allocator populate ``mem_map``517 correctly. No new ``arch/wasm32/`` files are expected for518 slub itself; the SLUB_TINY workaround is a Kconfig flip.519 520--------------------------------------------------------------------------------5216. vmalloc522--------------------------------------------------------------------------------523 524Per Q2: ``vmalloc(N)`` → ``__get_free_pages(get_order(N))``;525``vfree(p)`` → ``free_pages(p, get_order(known_size))``.526 527K3 implementation: a thin wrapper file528``arch/wasm32/mm/vmalloc.c`` overriding the upstream529``vmalloc`` / ``vfree`` / ``vzalloc`` / ``vmalloc_node`` /530``vmalloc_array`` entry points. The wrapper records the531order of each allocation in a small associative array532(keyed by returned pointer) so ``vfree`` can return the533right run size without taking a size argument.534 535The wrapper file is in ``arch/wasm32/mm/`` — *not* a fork of536``mm/vmalloc.c``. The upstream ``mm/vmalloc.c`` continues to537build (its internal machinery is unused; the symbols we538define here override the ones it would have provided via539``weak`` linkage).540 541--------------------------------------------------------------------------------5427. Per-CPU areas543--------------------------------------------------------------------------------544 545``setup_per_cpu_areas`` is the generic upstream version546(``mm/percpu.c::pcpu_alloc_alloc_info``-driven). Per-CPU547storage lives in ``env.kernel_memory`` like everything else;548each Worker reads ``smp_processor_id`` from its per-instance549wasm global (set by HardwareJS at Worker spawn — K4 work,550default 0 at K3) and indexes the per-CPU arrays551accordingly. The K3 ``num_online_cpus() == 1`` invariant552means only the CPU-0 area sees traffic during the K3553acceptance run.554 5557.1 Linker-anchor shim (K3 debt #4)556~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~557 558``__per_cpu_start`` / ``__per_cpu_end`` / ``__per_cpu_load``559are linker-script anchors in mainline Linux, bracketing the560``.data..percpu*`` family of segments via561``include/asm-generic/vmlinux.lds.h::PERCPU_INPUT``. wasm-ld562does not consume the linker script. K3 ships563``arch/wasm32/kernel/percpu_anchors.c`` as a discovery-time564workaround:565 566* Three ``char[1]`` placeholders in synthetic sections567 ``.data..percpu..aa_start`` and ``.data..percpu..zz_end``568 with naming chosen so wasm-ld's alphabetical-by-section-name569 emission order brackets the real percpu data between them.570* ``--no-merge-data-segments`` passed to ``wasm-ld`` in571 ``scripts/link-vmlinux-wasm.sh`` so the synthetic sections572 remain as separate data segments rather than being folded573 into a single ``.data``.574* The same trick brackets the five575 ``stop`` / ``dl`` / ``rt`` / ``fair`` / ``idle`` sched_class576 symbols. ``kernel/sched/core.c::sched_init`` BUG_ONs unless577 ``&stop_sched_class < &dl_sched_class < &rt_sched_class <578 &fair_sched_class < &idle_sched_class`` in memory order;579 mainline's ``SCHED_DATA`` macro enforces this via the580 linker script.581 582This is enough to satisfy the K3 invariants — ``static_size``583is set to a small positive value (1 + page alignment ≈ 4097)584so ``pcpu_setup_first_chunk`` doesn't BUG_ON. It is NOT585enough for correct per-CPU allocator math, which is why586``CONFIG_SLUB_TINY=y`` is also needed (§5 above and §0(c)587debt row 5). The real fix is the K5/K6 post-link Python pass588that emits real ``--defsym`` boundary symbols for both589percpu and sched_class — at which point both the590``percpu_anchors.c`` shim and the SLUB_TINY workaround591disappear together.592 593--------------------------------------------------------------------------------5948. Constraint on subsequent K-phases595--------------------------------------------------------------------------------596 597K4 cannot start until this section is committed and the K4598implementation notes reference each Q1–Q5 answer by number.599Any K4 commit that conflicts with §2 here forces a separate600doc commit first updating §2 — the spec doesn't silently601drift.602 603K5 / K6 inherit the constraint. K7 (userspace) is when Q1604(PAGE_SIZE) and Q5 (PTE-as-VMA) become observable to605external code; either of those changing after K7 is an606ABI break and requires a major-version bump.607