Stage MC-0b: real kernel spinlocks; sf_mutex no longer a no-op (FABRIC-3.10.8 §3.2)

- New spinlock primitive (spinlock.h/.c): CAS on an owner word (core+1),
  interrupts masked while any lock is held (state kept in the per-core
  block, so release order doesn't matter). Recursion, release-not-held,
  held-at-switch and destroyed-while-held are counted and logged, never
  silent. Locks are no-ops until the boot core's block is installed
  (riscv64's monolithic early boot prints before sk_percpu_boot_init()).
- Kernel locks in a documented order: hermes, switch, registry, doe, log,
  console, heap, vmm, pmm. sf_mutex (dict_lock/tuning_lock) uses the same
  lock word.
- Hermes: public functions over *_locked internals; never held across the
  drain's vm_interpret_kind(); switch notifications after dropping the
  lock; event payload moved off static storage. Switch table and registry
  never call out while held (slot/entry copied under the lock).
- No lock crosses a switch: checked at context switch (leave, resume) and
  the idle point.
- Probe (removed): every lock balances on all three ISAs; 0 held at
  switch over 7.5M-51M checks; 0 recursions, 0 bad releases.
- Standard sequence on amd64/aarch64/riscv64: hashes and G byte-identical
  to MC-0a, MINT/WIREBIND/USE ok, conservation true, 0 idle growth.
- Block subsystem split out as MC-0b2 (Captain Bob's ruling).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-25 16:43:19 -04:00
co-authored by Claude Opus 5.5
parent 8409e376cf
commit 5bc7b4dee7
28 changed files with 56110 additions and 274 deletions
+16
View File
@@ -49,6 +49,14 @@ typedef struct sk_percpu {
struct sk_percpu *self; /* must stay first: amd64 reads it as %gs:0 */
uint32_t core_id; /* 0 = the boot core (the system core, MC-R1) */
struct VM *current_vm; /* who is running on this core (switch.c) */
/* MC-0b (spinlock.c): locks this core holds right now, and the
* interrupt state to restore when that count returns to 0. Kept here,
* not in each lock, so interrupts come back correctly whatever order
* the locks are released in. */
uint32_t lock_depth;
uint64_t saved_irq;
uint64_t lock_acquired; /* every acquire on this core, ever */
uint64_t lock_released; /* every release on this core, ever */
} sk_percpu_t;
/* Installs the boot core's block in this core's per-CPU register. Call
@@ -56,6 +64,14 @@ typedef struct sk_percpu {
* arch_early_init(): on amd64 that reloads GS, which would reset the base). */
void sk_percpu_boot_init(void);
/* 1 once the boot core's block is installed (MC-0b). Before that the
* per-CPU register still holds whatever the firmware left there -- on
* riscv64 the monolithic loader's kernel_main() prints from
* arch_early_init() with tp still the firmware's -- so the spinlocks
* (spinlock.c) do nothing until it is set: early boot is one core with
* interrupts off. */
extern volatile int sk_percpu_ready;
/* This core's block. Valid from sk_percpu_boot_init() on. */
static inline sk_percpu_t *sk_percpu(void)
{