FABRIC-3.10.8 MC-2: observers stay on core 0

The decision's observer pass runs only on core 0; a switch into an
observer on any other core is refused and counted (a guard, 0 in every
run). Probe posing as core 1: no observer chosen, switch refused 0 -> 1;
as core 0 the same decision returns Hestia. 3-ISA acceptance clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-26 14:24:47 -04:00
co-authored by Claude Opus 5.5
parent 3c07636ecd
commit c8b754fd80
12 changed files with 36665 additions and 2 deletions
+17
View File
@@ -37,6 +37,7 @@
#include "starkernel/percpu.h" /* FABRIC-3.10.8 MC-0a -- this core's current VM */
#include "starkernel/spinlock.h" /* FABRIC-3.10.8 MC-0b -- no lock crosses a switch */
#include "starkernel/smp.h" /* FABRIC-3.10.8 MC-1 -- sk_smp_report() */
#include "log.h" /* FABRIC-3.10.8 MC-2 -- the refusal report */
#include <stdint.h>
/* Arch-specific (switch.S in each arch dir). sk_vm_switch_prime() builds
@@ -59,6 +60,10 @@ extern uint64_t sk_vm_switch_prime(uint64_t stack_top, void *entry);
* who most recently switched into it does. */
static VM *g_switch_entry_vm;
/* FABRIC-3.10.8 MC-2: switches refused because they would have entered an
* observer on a core other than core 0. A guard -- expected to stay 0. */
static uint64_t g_observer_refusals, g_observer_refusals_reported;
/* FABRIC-3.md §XXVIII Stage 3 follow-on (2026-09-14): which VM is
* currently running, from the switch mechanism's own point of view --
* independent of vm_log_attributed_vm() (vm_core.c), which is scoped to
@@ -233,6 +238,11 @@ void sk_vm_idle_point(int may_drain_self) {
sk_lock_check_none_held("idle point");
sk_lock_report();
sk_smp_report(); /* FABRIC-3.10.8 MC-1: a parked core that woke */
if (g_observer_refusals != g_observer_refusals_reported) { /* MC-2 */
g_observer_refusals_reported = g_observer_refusals;
log_message(LOG_ERROR, "SWITCH: observer refused off core 0 x%llu",
(unsigned long long)g_observer_refusals);
}
if (!self || self != (VM *)sk_get_mama_vm()) return;
if (self->frames.count != 0) return; /* inside a word (KEY/EXPECT): not idle */
@@ -267,12 +277,19 @@ int sk_vm_parked_idle(const VM *vm) {
vm_uuid_equal(vm->frames.entries[0].vm_id, vm->stadium_vm_id);
}
uint64_t sk_vm_switch_observer_refusals(void) { return g_observer_refusals; }
int sk_vm_context_switch(VM *from, VM *to) {
uint64_t new_sp;
if (!from || !to || to->native_stack_top == 0) {
return -1;
}
/* MC-2 (MC-R1): the observers run on core 0 only. */
if (sk_percpu()->core_id != 0 && sk_vm_switch_signal_is_observer(to->stadium_vm_id)) {
__atomic_add_fetch(&g_observer_refusals, 1, __ATOMIC_RELAXED);
return -1;
}
if (to->native_stack_saved_sp == 0) {
/* First-ever entry: synthesize the initial frame. */