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:
co-authored by
Claude Opus 5.5
parent
3c07636ecd
commit
c8b754fd80
@@ -964,3 +964,24 @@ The boot hart HSM chose (0) is the one OpenSBI names as `Boot HART ID`.
|
||||
- `sk_vm_switch_decide()` returns no observer; it returns a participant or nothing.
|
||||
- `sk_vm_context_switch()` to Hestia is refused, and the refusal count goes from 0 to 1.
|
||||
- With `core_id` back at 0, the same decision returns Hestia.
|
||||
|
||||
### 6.1 MC-2 — executed (2026-09-26)
|
||||
|
||||
**Change.**
|
||||
- **`capsule_vm_switch_signal.c`:** the observer pass of `sk_vm_switch_decide()` runs only when `sk_percpu()->core_id == 0`.
|
||||
- **`switch.c`:** `sk_vm_context_switch()` refuses, before touching any state, to enter an observer on any other core. It counts the refusal (`sk_vm_switch_observer_refusals()`, declared in `switch.h`). The idle point logs `SWITCH: observer refused off core 0 xN` when the count changes.
|
||||
|
||||
**Probe** (temporary `MC2-PROBE` word; amd64, `logs/20260926-135242`). One console line queued a message for Hestia (`S" 1 DROP" S" Hestia" SEND .`) and then ran the probe, so Hestia held an event.
|
||||
- The probe printed: `MC2 PROBE: as core 1 decide -> nothing; switch to Hestia rc=-1, refusals 0 -> 1; as core 0 decide -> Hestia`.
|
||||
- The idle point then logged `SWITCH: observer refused off core 0 x1`.
|
||||
- The decisions were made with no calling VM, because Hera, mid-line, would keep the CPU as an observer in progress. There were no workers, so "nothing" is the participant pass's correct answer.
|
||||
|
||||
**Standard sequence** (clean build, probe reverted; the tree is byte-identical to the saved stage diff):
|
||||
|
||||
| ISA | log | result |
|
||||
|---|---|---|
|
||||
| amd64 | `logs/20260926-135440` | K-10 hashes, G A=40 B=40 byte-identical, HHH, all ok `3`, ledger unchanged, 0 B idle, 0 / 0; 2 cores online; **0 observer refusals**, 0 park wakes, 0 `LOCK:` |
|
||||
| aarch64 | `logs/20260926-140427` | same |
|
||||
| riscv64 | `logs/20260926-141508` | same; 0 `timed out` |
|
||||
|
||||
**MC-2 is complete.** Next: MC-3, where worker cores claim through the shared decision. It is the stage that makes the extra cores do work, and it needs its own design section first: the claim by compare-and-swap, per-core idle contexts and beats, wake by IPI with each core's interrupt controller, and the §5.3 findings.
|
||||
|
||||
@@ -42,10 +42,16 @@
|
||||
|
||||
#ifdef __STARKERNEL__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct VM;
|
||||
|
||||
int sk_vm_context_switch(struct VM *from, struct VM *to);
|
||||
|
||||
/* FABRIC-3.10.8 MC-2: how many switches were refused because they would
|
||||
* have entered an observer on a core other than core 0 (a guard; 0). */
|
||||
uint64_t sk_vm_switch_observer_refusals(void);
|
||||
|
||||
/* FABRIC-3.md §XXVIII Stage 3 follow-on (2026-09-14): which VM is
|
||||
* currently physically running, tracked by the switch mechanism itself --
|
||||
* see switch.c's own doc comment on g_switch_current_vm for why this
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,7 @@
|
||||
#include "starkernel/vm/bootstrap/sk_vm_bootstrap.h" /* sk_get_mama_vm() */
|
||||
#include "starkernel/repl.h" /* sk_console_key_available() -- Hera's mail (C-5) */
|
||||
#include "starkernel/spinlock.h" /* FABRIC-3.10.8 MC-0b -- g_lock_switch */
|
||||
#include "starkernel/percpu.h" /* FABRIC-3.10.8 MC-2 -- the deciding core */
|
||||
#include <stddef.h>
|
||||
|
||||
/* FABRIC-3.6.md task 3.1 (2026-09-21, ruled B2 / FABRIC-3.5.md §XLV.2):
|
||||
@@ -341,8 +342,10 @@ VM *sk_vm_switch_decide(VM *self, int self_competes) {
|
||||
|
||||
/* Observers with events go ahead of every participant, at this clean
|
||||
* boundary; among themselves, the one waiting longest (unique stamps,
|
||||
* so still independent of walk order). No heat involved. */
|
||||
for (i = 0; slot_copy(i, &s); i++) {
|
||||
* so still independent of walk order). No heat involved.
|
||||
* FABRIC-3.10.8 MC-2 (MC-R1): observers live on core 0, the system
|
||||
* core; any other core compares participants only. */
|
||||
for (i = 0; sk_percpu()->core_id == 0 && slot_copy(i, &s); i++) {
|
||||
VMRegistryEntry e;
|
||||
VM *v;
|
||||
uint64_t stamp;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user