/* StarForth — Steady-State Virtual Machine Runtime Copyright (c) 2023–2025 Robert A. James All rights reserved. This file is part of the StarForth project. Licensed under the StarForth License, Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: https://github.com/star.4th@proton.me/StarForth/LICENSE.txt This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. See the License for the specific language governing permissions and limitations under the License. */ /** * stadium_blocks.h - Block patrons on the Stadium (FABRIC-3.md §B/§D, * MIGRATE punch-list item) * * The block-specific layer on top of the generic L0 engine (stadium.h), same * relationship stadium_words.h/.c already has: nothing in stadium.c/.h knows * a block patron exists -- it only ever sees cell_index, VMUuid, and * StadiumPatronHeader. This file is where "block" becomes a concrete * meaning: an (owning quota slot, LBN) -> cell_index map, the starter-grant * admission rule (mirrors stadium_word_dispatch()'s Option B exactly), and * the reservoir-quantum touch/cool that feeds and drains a resident block's * Stadium heat. * * Unlike words, LBN is not densely bounded (block_subsystem.c's unified LBN * space spans RAM/RAMDRIVE/DISK/USB and can be large), so the map here is a * fixed-capacity open-addressing hash table sized off stadium_cell_count() * at init, not a dense per-VM array -- see stadium_blocks.c for the layout. * A block's actual 1024 content bytes are never copied into a Stadium cell; * they stay exactly where block_subsystem.c already keeps them. The cell * only ever carries identity (the LBN) and heat/bookkeeping, same as a word * patron's cell never carries the word's own dictionary entry. */ #ifndef STARKERNEL_VM_STADIUM_BLOCKS_H #define STARKERNEL_VM_STADIUM_BLOCKS_H #ifdef __STARKERNEL__ #include #include "starkernel/vm_uuid.h" /* * stadium_blocks_init - Allocates and zeroes the (quota slot, LBN) -> * cell_index hash table (capacity computed from stadium_cell_count() * * STADIUM_BLOCK_TRACK_CAP_MULT at call time, kmalloc'd). Must be called * after stadium_boot_init() -- so stadium_cell_count() is non-zero -- and * before any block ever dispatches; the real boot site is immediately after * the existing stadium_words_init() call (kernel_main.c), same M7/M7.1 * ordering. Not safe to call twice -- guarded internally as a no-op if * already initialized, same convention as stadium_words_init(). */ void stadium_blocks_init(void); /* * stadium_block_dispatch - The per-touch entry point, called from * block_word_block()/block_word_buffer()/block_word_update() * (src/word_source/block_words.c) -- mirroring stadium_word_dispatch()'s * call pattern and cooling/admission logic 1:1, keyed by LBN instead of * word_id. * * If (vm_id, lbn) is already resident: applies the same redirected Loop #3 * cooling stadium_word_dispatch() applies (fraction of the cell's own * current heat, scaled by elapsed_ticks since this block's own last touch -- * STADIUM_BLOCK_COOL_RATE_Q48), crediting the cooled amount back to vm_id's * reservoir, then pulls STADIUM_BLOCK_HEAT_QUANTUM from the reservoir into * the cell -- clamped to the reservoir's actual balance AND to the same * Q48_ONE / 3 floor stadium_word_dispatch() enforces, so block-touch * admission alone can never starve other reservoir consumers sharing the * same VM. * * If not resident (or the table's entry is stale -- self-healing check * against the cell's discriminator bit and identity, same pattern * resolve_resident_cell() uses in stadium_words.c): attempts starter-grant * admission -- pulls STADIUM_BLOCK_HEAT_QUANTUM (same floor), builds an * unpinned MIGRATE candidate (identity = lbn, mass = 1, payload unused -- * the block's real content is never copied here), calls stadium_admit(). On * refusal, pushes the pulled quantum back (rollback). On success, records * the mapping. If the hash table itself is full and has no slot for this * (vm_id, lbn) pair, this touch is silently skipped -- Stadium's own * capacity already bounds real residency, so a table miss under load is * graceful degradation, not an error. * * No-op if vm_id holds no Stadium quota, or stadium_blocks_init() has not * run. * * @param vm_id Owning VM -- vm->stadium_vm_id at every call site, * same quota-isolation reasoning stadium_word_ * dispatch() already documents (LBN numbering is * global, not per-VM, but quota scoping still keeps * two VMs' admissions from evicting each other). * @param lbn Logical block number being touched. * @param heartbeat_ticks Current vm->heartbeat.tick_count. */ void stadium_block_dispatch(VMUuid vm_id, uint32_t lbn, uint64_t heartbeat_ticks); /* * stadium_blocks_print_boot_diagnostics - Console output mirroring * stadium_words_print_boot_diagnostics(): promotions/evictions for vm_id's * own block-touch table, plus a conservation check * (Σ(resident block heat) + reservoir against Q48_ONE is NOT a standalone * invariant here -- word-execution heat and any other resident shares the * same reservoir, so this prints the block-only resident sum as a * diagnostic term, not a claim that it alone should equal Q48_ONE). * * @param vm_id The VM whose block-touch table/reservoir to read. */ void stadium_blocks_print_boot_diagnostics(VMUuid vm_id); #endif /* __STARKERNEL__ */ #endif /* STARKERNEL_VM_STADIUM_BLOCKS_H */