/* 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. */ /** * spinlock.h - Kernel spinlocks (FABRIC-3.10.8 Stage MC-0b). * * One lock word: 0 = free, core_id + 1 = held by that core. Taken by * compare-and-swap; interrupts are masked on the owning core while it * holds any lock (the first lock taken saves the interrupt state in the * per-core block, the last one released restores it -- percpu.h). * * The rule that makes this correct: **no lock is held across a context * switch or into the idle point.** Every switch and every idle point * checks it (sk_lock_check_none_held()) and counts a violation. * * Failures are counted, never silent (sk_lock_report() logs new ones): * - recursion: this core takes a lock it already holds. On one core, * "the owner is this core" can't tell a nested call from a second VM * on the same core, so it is never treated as legitimate. The call is * let through (spinning would hang the core) and counted; * - bad release: releasing a lock this core doesn't hold; * - held at switch: a switch or idle point reached with a lock held. * * sf_mutex_t (the vendored VM's dict_lock/tuning_lock) uses the same lock * word through sk_lockword_acquire()/sk_lockword_release() (shim.c). * * THE KERNEL LOCKS, in acquisition order (a lock may be taken while * holding only locks above it; MC-3's deadlock freedom rests on this): * * g_lock_hermes kernel-Hermes arena, queues, channels, ledger * g_lock_switch switch-signal slot table and stamps * (capsule_vm_switch_signal.c) * g_lock_registry VM registry list and entries (capsule_birth.c) * g_lock_doe DoE region (doe_region.c) * g_lock_log log region (log_region.c) * g_lock_console console output and line state (console.c) * g_lock_heap kmalloc heap * g_lock_vmm page tables (allocates tables from the PMM) * g_lock_pmm physical page bitmap * * The switch table and the registry never call out while held: their walks * copy one entry under the lock and judge it outside. Hermes hands the * switch module its notifications after dropping its own lock (they read * Hermes queues back). Stadium's lock comes with MC-0d; it sits below * Hermes (Hermes admits and evicts cells). */ #ifndef STARKERNEL_SPINLOCK_H #define STARKERNEL_SPINLOCK_H #include typedef struct sk_spinlock { volatile uint32_t owner; /* 0 = free, else core_id + 1 */ uint64_t acquired; /* updated while held, so exact */ uint64_t released; } sk_spinlock_t; #define SK_SPINLOCK_INIT { 0, 0, 0 } void sk_lockword_acquire(volatile uint32_t *word); void sk_lockword_release(volatile uint32_t *word); void sk_spin_lock(sk_spinlock_t *l); void sk_spin_unlock(sk_spinlock_t *l); /* 1 if this core holds `l`. For paths that must not spin on a lock their * own core may hold (the fatal exception printers). */ int sk_spin_held_here(const sk_spinlock_t *l); /* Every switch and idle point: counts a violation if this core holds any * lock. `where` names the site in the log. */ void sk_lock_check_none_held(const char *where); /* Logs any failure counted since the last report. Call only with no lock * held (the idle point does). */ void sk_lock_report(void); uint64_t sk_lock_recursions(void); uint64_t sk_lock_bad_releases(void); uint64_t sk_lock_held_at_switch(void); extern sk_spinlock_t g_lock_registry; extern sk_spinlock_t g_lock_switch; extern sk_spinlock_t g_lock_hermes; extern sk_spinlock_t g_lock_doe; extern sk_spinlock_t g_lock_log; extern sk_spinlock_t g_lock_console; extern sk_spinlock_t g_lock_heap; extern sk_spinlock_t g_lock_pmm; extern sk_spinlock_t g_lock_vmm; #endif /* STARKERNEL_SPINLOCK_H */