Stage K-4: quota slots released to the best donor; message cells out of the admission fallback (FABRIC-3.10.8 §4.4)

- stadium_release_quota(): evict every patron but pending messages, mark
  the slot retiring; stadium_retire_sweep() releases a retiring slot once
  nothing is resident -- free cells to the best donor (ruled), word row
  and block-table entries cleared, slot free for reuse. Swept at release
  and after evictions while any slot is retiring.
- Called on KILL and reap, and for the nine synthetic self-test ids after
  the boot self-tests. Hera's slot never released.
- Admission fallback skips DELIVER (message) cells.
- Probe (removed): slots in use == live VMs after boot and after 56-64
  churned VMs on all three ISAs; forced fallback kept the message cell
  resident and intact; conserved everywhere; K-1's post-churn Hermes
  refusal gone (1 2 + . -> 3 on the churned system). Heap growth per
  28-VM churn halved (~3.4 KB/VM still retained -- open as K-8).
- Standard sequence on amd64/aarch64/riscv64: hash, G, ledger unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-26 05:44:33 -04:00
co-authored by Claude Opus 5.5
parent 9e41fb03ab
commit 1166713cc9
21 changed files with 56959 additions and 9 deletions
+62
View File
@@ -538,3 +538,65 @@ The results are identical on all three ISAs:
3. The `CONNECT-HERMES` word (`mama_forth_words.c` ~2257) still births and names a Hermes VM. 3. The `CONNECT-HERMES` word (`mama_forth_words.c` ~2257) still births and names a Hermes VM.
**K-3 closes §8 defect 4.** **K-3 closes §8 defect 4.**
### 4.4 K-4 — quota slots released; message cells kept out of the admission fallback (2026-09-26)
**Change** (stadium.c; ruling: released cells go to the best donor). Release happens in two steps, because a dead VM's slot can still hold cells for messages it **sent** that sit in other VMs' queues.
1. **`stadium_release_quota(vm_id)`**, under `g_lock_stadium`, then the slot's lock:
- every resident patron except pending messages (DELIVER) is evicted, its heat back to the slot's reservoir; pins are cleared first, since a dead VM's pins no longer matter;
- the slot is marked **retiring**, and is never a donor while retiring;
- a patron eviction refuses (one holding another) stays resident and is logged.
2. **`stadium_retire_sweep()`** runs at release, and after any eviction while a slot is retiring, which is how a retiring slot's last message leaving completes its release. It is one atomic load when nothing is retiring. For each retiring slot with nothing resident:
- detach the free list;
- clear its word row (`stadium_words_slot_released()`, under the slot lock) and its block-table entries (`stadium_blocks_slot_released()`, under the global lock);
- hand the cells to the best donor (one slot lock at a time, each cell's owner set to the donor);
- clear the slot and publish `in_use = 0`.
The dead VM's Q48_ONE share leaves with the slot. The donor gains free cells only, with no heat, so its own sum is unchanged.
- **Callers:**
- KILL and reap release the VM's slot after its Hermes departure;
- `kernel_main.c` releases the nine synthetic self-test ids (hi 0, lo 1, 3, 7, 11, 13, 15, 16, 19, 20) after the last self-test, and prints `Stadium: self-test quotas released=`;
- Hera's slot is never released.
- **MC-0d finding 3:** the admission fallback skips message (DELIVER) cells. A message leaves Stadium only through its release.
**Probe** (temporary `K4-PROBE` / `K4-FALLBACK` words, removed before commit). They print:
- quota slots in use (and retiring) against live VMs, conservation on every live VM, slots released so far, and heap used;
- **the forced fallback:**
1. grant a synthetic slot T, and send one real message from it to a synthetic receiver;
2. fill T's free list with zero-heat fillers until admission refuses;
3. admit a denser candidate (heat 1, pulled from T's reservoir) and check the message cell;
4. clean up: the receiver departs (its mail released), then T is released.
Then two `churn.sh` rounds (28 worker births, jobs and KILLs each), `K4-PROBE` after each, and the standard sequence **on the churned system**, the direct test of K-1's post-churn refusal.
| ISA | log | self-test quotas released | slots in use = live VMs (boot / churn 1 / churn 2) | fallback: denser admitted, message cell resident and intact | post-churn `1 2 + .` |
|---|---|---|---|---|---|
| amd64 | `logs/20260926-035738` | 9 | 3=3 / 3=3 / 4=4 † | yes, yes, yes | `3` |
| aarch64 | `logs/20260926-042229` | 9 | 3=3 / 3=3 / 3=3 | yes, yes, yes | `3` |
| riscv64 | `logs/20260926-044430` | 9 | 3=3 / 3=3 / 3=3 | yes, yes, yes | `3` |
- Every live VM was conserved at every reading, with 0 Hermes audit failures and 0 slots left retiring.
- † amd64: my script's timeout cut the second churn short (36 kills). I ran a third full round, 64 churned VMs in total, past K-1's 56; one worker from the interrupted round was still alive at the final reading.
**K-1's carried criterion (heap), re-measured:**
| Heap growth per 28-VM churn round | round 1 | round 2 |
|---|---|---|
| amd64, K-1 (before K-4) | +324,960 | +201,696 |
| aarch64, K-4 | +95,584 | +95,200 |
| riscv64, K-4 | +95,584 | +95,200 |
The per-round growth halved, but about **3.4 KB is still retained per churned VM**, so the "within one allocation" criterion is **still not met**. Registry nodes are kept by design (a node per VM ever born); the rest is unidentified. It stays open as **K-8: find the ~3.4 KB/VM residual**.
**K-1's post-churn Hermes refusal is gone:** after 56–64 churned VMs, rajames's `1 2 + .` returns `3` on every ISA. That confirms the refusal was the quota leak.
**Standard sequence** (clean build, with the probe reverted; the tree is byte-identical to the saved stage diff).
| ISA | log | O-A (G, PREC, LOAD) | M7.1a hash | MINT / WIREBIND / USE / `1 2 + .` | idle growth | UNKNOWN / faults |
|---|---|---|---|---|---|---|
| amd64 | `logs/20260926-050559` | A=40 B=40, HHH, ok | `0x99b01134c3d3049c` | all ok, `3` | 0 B / 20 s | 0 / 0 |
| aarch64 | `logs/20260926-051834` | A=40 B=40, HHH, ok | `0x99b01134c3d3049c` | all ok, `3` | 0 B / 20 s | 0 / 0 |
| riscv64 | `logs/20260926-053135` | A=40 B=40, HHH, ok | `0x99b01134c3d3049c` | all ok, `3` | 0 B / 20 s | 0 / 0 |
- The G string and the ledger line (the K-3 baseline) are unchanged.
- **K-4 closes MC-0d findings 2 and 3.** K-1's heap criterion carries on as K-8.
+12
View File
@@ -622,6 +622,18 @@ int stadium_cell_decay(size_t cell_index, uint64_t factor_q48, uint64_t *consume
* session_set_pinned(), the sole pin choke point). -1 if out of range. */ * session_set_pinned(), the sole pin choke point). -1 if out of range. */
int stadium_cell_set_pin(size_t cell_index, int pinned); int stadium_cell_set_pin(size_t cell_index, int pinned);
/*
* FABRIC-3.10.8 K-4: quota slots are released (cells to the best donor,
* ruled 2026-09-26). stadium_release_quota() retires vm_id's slot (every
* patron but its pending messages evicted); stadium_retire_sweep() releases
* each retiring slot once nothing is resident. Call release on VM death
* (KILL, reap) and when a self-test's synthetic id is done. Hera's slot is
* never released. Returns 0, or -1 (no such slot, already retiring, Hera).
*/
int stadium_release_quota(VMUuid vm_id);
void stadium_retire_sweep(void);
uint64_t stadium_slots_released(void);
#endif /* __STARKERNEL__ */ #endif /* __STARKERNEL__ */
#endif /* STARKERNEL_VM_STADIUM_H */ #endif /* STARKERNEL_VM_STADIUM_H */
+5
View File
@@ -119,6 +119,11 @@ void stadium_block_dispatch(VMUuid vm_id, uint32_t lbn, uint64_t heartbeat_ticks
*/ */
void stadium_blocks_print_boot_diagnostics(VMUuid vm_id); void stadium_blocks_print_boot_diagnostics(VMUuid vm_id);
/* FABRIC-3.10.8 K-4: quota slot `slot` is being released -- tombstone its
* block-table entries and zero its statistics, so the slot's next VM
* inherits none of them. Caller holds g_lock_stadium. */
void stadium_blocks_slot_released(int slot);
#endif /* __STARKERNEL__ */ #endif /* __STARKERNEL__ */
#endif /* STARKERNEL_VM_STADIUM_BLOCKS_H */ #endif /* STARKERNEL_VM_STADIUM_BLOCKS_H */
+5
View File
@@ -181,6 +181,11 @@ uint64_t stadium_words_row_grow_failures(void);
*/ */
void stadium_words_print_boot_diagnostics(VMUuid vm_id); void stadium_words_print_boot_diagnostics(VMUuid vm_id);
/* FABRIC-3.10.8 K-4: quota slot `slot` is being released -- free its word
* row and zero its statistics, so the slot's next VM starts clean. Caller
* holds the slot's lock (stadium_retire_sweep()). */
void stadium_words_slot_released(int slot);
#endif /* __STARKERNEL__ */ #endif /* __STARKERNEL__ */
#endif /* STARKERNEL_VM_STADIUM_WORDS_H */ #endif /* STARKERNEL_VM_STADIUM_WORDS_H */
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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+7
View File
@@ -450,6 +450,10 @@ void capsule_vm_force_reap(VMUuid vm_id) {
capsule_vm_hermes_depart(vm_id, entry->name, was_observer); capsule_vm_hermes_depart(vm_id, entry->name, was_observer);
} }
/* FABRIC-3.10.8 K-4: its Stadium quota slot is released (after the
* Hermes departure above, which returns its pending mail). */
(void)stadium_release_quota(vm_id);
capsule_parity_log_kill(vm_id, entry->name); capsule_parity_log_kill(vm_id, entry->name);
console_puts("REAP: "); console_puts("REAP: ");
@@ -635,6 +639,9 @@ int capsule_vm_kill(const char *name) {
capsule_vm_hermes_depart(vm_id, entry->name, was_observer); capsule_vm_hermes_depart(vm_id, entry->name, was_observer);
} }
/* FABRIC-3.10.8 K-4: its Stadium quota slot is released. */
(void)stadium_release_quota(vm_id);
sk_spin_lock(&g_lock_registry); sk_spin_lock(&g_lock_registry);
entry->vm_ptr = (void *)0; entry->vm_ptr = (void *)0;
entry->state = VM_STATE_DEAD; entry->state = VM_STATE_DEAD;
+17
View File
@@ -1846,6 +1846,23 @@ static void kernel_main_deep(BootInfo *boot_info) {
} }
} }
/* FABRIC-3.10.8 K-4: the self-tests above granted Stadium quotas to
* synthetic ids (hi 0; lo 1, 3, 7, 11, 13, 15, 16, 19, 20). They are
* released here, now that quota slots can be: before K-4 each kept its
* slot and cells for the whole boot. An id whose grant was refused has
* no slot, and its release is a no-op. */
{
static const uint64_t synthetic_lo[] = { 1, 3, 7, 11, 13, 15, 16, 19, 20 };
size_t k, released = 0;
for (k = 0; k < sizeof synthetic_lo / sizeof synthetic_lo[0]; k++) {
VMUuid id;
id.hi = 0;
id.lo = synthetic_lo[k];
if (stadium_release_quota(id) == 0) released++;
}
print_uint("Stadium: self-test quotas released=", (uint64_t)released);
}
/* Decided 2026-09-05: no console for the running system unless a /* Decided 2026-09-05: no console for the running system unless a
* thumbdrive is present -- headless by default (EMERGENCY_CONSOLE_ * thumbdrive is present -- headless by default (EMERGENCY_CONSOLE_
* ENABLED off), reusing that flag's own existing "does this build * ENABLED off), reusing that flag's own existing "does this build
+168 -9
View File
@@ -44,6 +44,9 @@
#include "block_subsystem.h" /* blk_flush() -- STADIUM_BEHAVIOUR_MIGRATE's real write-back action */ #include "block_subsystem.h" /* blk_flush() -- STADIUM_BEHAVIOUR_MIGRATE's real write-back action */
#include "starkernel/session.h" /* session_register()/session_set_pinned() -- FABRIC-2.md §H.12 step 4 */ #include "starkernel/session.h" /* session_register()/session_set_pinned() -- FABRIC-2.md §H.12 step 4 */
#include "starkernel/spinlock.h" /* FABRIC-3.10.8 MC-0d -- per-slot locks, g_lock_stadium */ #include "starkernel/spinlock.h" /* FABRIC-3.10.8 MC-0d -- per-slot locks, g_lock_stadium */
#include "starkernel/vm/stadium_words.h" /* K-4 -- stadium_words_slot_released() */
#include "starkernel/vm/stadium_blocks.h" /* K-4 -- stadium_blocks_slot_released() */
#include "starkernel/vm_uuid.h" /* K-4 -- vm_uuid_is_hera() */
static StadiumCell *stadium_cell_array = (StadiumCell *)0; static StadiumCell *stadium_cell_array = (StadiumCell *)0;
static uint8_t *stadium_bitmap = (uint8_t *)0; static uint8_t *stadium_bitmap = (uint8_t *)0;
@@ -113,6 +116,12 @@ typedef struct {
* have decayed away (recorded by kernel-Hermes, * have decayed away (recorded by kernel-Hermes,
* FABRIC-3.6.md task 2.7). Full invariant: * FABRIC-3.6.md task 2.7). Full invariant:
* Σ(resident) + reservoir + consumed == Q48_ONE. */ * Σ(resident) + reservoir + consumed == Q48_ONE. */
int retiring; /* FABRIC-3.10.8 K-4: its VM is gone (stadium_release_
* quota()); every patron but its pending messages has
* been evicted, and the slot is released -- cells to
* the best donor, slot free for reuse -- once the last
* of those messages leaves (stadium_retire_sweep()).
* Never a donor meanwhile. */
volatile uint32_t lock; /* FABRIC-3.10.8 MC-0d: this slot's own lock (a volatile uint32_t lock; /* FABRIC-3.10.8 MC-0d: this slot's own lock (a
* spinlock.h lock word). Covers every field above, * spinlock.h lock word). Covers every field above,
* and this slot's cells: their headers, owner and * and this slot's cells: their headers, owner and
@@ -312,6 +321,7 @@ int stadium_boot_init(void) {
quotas[i].reservoir = 0; quotas[i].reservoir = 0;
quotas[i].consumed = 0; quotas[i].consumed = 0;
quotas[i].lock = 0; quotas[i].lock = 0;
quotas[i].retiring = 0;
} }
} }
quotas[0].vm_id = vm_uuid_hera(); quotas[0].vm_id = vm_uuid_hera();
@@ -558,6 +568,9 @@ int stadium_evict_credit(size_t cell_index, uint64_t *credited) {
if (slot < 0) return -1; if (slot < 0) return -1;
rc = evict_locked(cell_index, credited); rc = evict_locked(cell_index, credited);
slot_unlock(slot); slot_unlock(slot);
/* K-4: this may have been a retiring slot's last message. Cheap when
* nothing is retiring (one atomic load). */
stadium_retire_sweep();
return rc; return rc;
} }
@@ -645,6 +658,10 @@ size_t stadium_admit_in_slot(int slot, const StadiumPatronHeader *candidate) {
if (h->flags & STADIUM_FLAG_PIN) continue; if (h->flags & STADIUM_FLAG_PIN) continue;
if (h->contains != STADIUM_CONTAINS_NONE) continue; if (h->contains != STADIUM_CONTAINS_NONE) continue;
/* FABRIC-3.10.8 K-4: a message (DELIVER) cell leaves only through
* its release (kernel-Hermes). Evicted here, its message would keep
* pointing at a cell another patron then takes. */
if (h->behaviour == (uint8_t)STADIUM_BEHAVIOUR_DELIVER) continue;
{ {
uint64_t d = stadium_density(idx); uint64_t d = stadium_density(idx);
@@ -740,6 +757,7 @@ int stadium_grant_quota(VMUuid new_vm_id, VMUuid from_vm_id) {
stadium_quotas[new_slot].reservoir = Q48_ONE; stadium_quotas[new_slot].reservoir = Q48_ONE;
stadium_quotas[new_slot].consumed = 0; stadium_quotas[new_slot].consumed = 0;
stadium_quotas[new_slot].lock = 0; stadium_quotas[new_slot].lock = 0;
stadium_quotas[new_slot].retiring = 0;
__atomic_store_n(&stadium_quotas[new_slot].in_use, 1, __ATOMIC_RELEASE); __atomic_store_n(&stadium_quotas[new_slot].in_use, 1, __ATOMIC_RELEASE);
sk_spin_unlock(&g_lock_stadium); sk_spin_unlock(&g_lock_stadium);
@@ -762,25 +780,166 @@ int stadium_grant_quota(VMUuid new_vm_id, VMUuid from_vm_id) {
* holds a quota yet (Stadium not initialized, or called before * holds a quota yet (Stadium not initialized, or called before
* Hera's own boot-time grant). * Hera's own boot-time grant).
*/ */
VMUuid stadium_best_donor(void) { /* The in-use, non-retiring slot with the most free cells, other than
* `exclude` (-1 for none). -1 if there is none. K-4: a retiring slot is
* never a donor -- its cells are on their way out. */
static int best_donor_slot(int exclude) {
size_t i; size_t i;
size_t best_count = 0; size_t best_count = 0;
int found = 0; int best = -1;
VMUuid best = vm_uuid_none();
if (!stadium_initialized) return best;
for (i = 0; i < stadium_max_vm_count_val; i++) { for (i = 0; i < stadium_max_vm_count_val; i++) {
if (!stadium_quotas[i].in_use) continue; if (!stadium_quotas[i].in_use || stadium_quotas[i].retiring) continue;
if (!found || stadium_quotas[i].free_count > best_count) { if ((int)i == exclude) continue;
if (best < 0 || stadium_quotas[i].free_count > best_count) {
best_count = stadium_quotas[i].free_count; best_count = stadium_quotas[i].free_count;
best = stadium_quotas[i].vm_id; best = (int)i;
found = 1;
} }
} }
return best; return best;
} }
VMUuid stadium_best_donor(void) {
int slot;
if (!stadium_initialized) return vm_uuid_none();
slot = best_donor_slot(-1);
return (slot < 0) ? vm_uuid_none() : stadium_quotas[slot].vm_id;
}
/*
* FABRIC-3.10.8 K-4: quota slots are released (ruled 2026-09-26: the cells
* go to the best donor). They never were -- a dead VM kept its slot and
* every cell in it, and each birth halved the best donor, so churn starved
* later births (a console VM born after 56 churned workers couldn't admit a
* message cell).
*
* Two steps, because a dead VM's slot can still hold cells for messages it
* SENT that sit in other VMs' queues: evicting those would leave kernel-
* Hermes pointing at cells another patron then takes.
* 1. stadium_release_quota(): every other patron is evicted (its heat back
* to the slot's own reservoir), and the slot is marked retiring.
* 2. stadium_retire_sweep(): a retiring slot with nothing left resident is
* released -- its free cells join the best donor's free list, its word
* row and block-table entries are cleared, and the slot is free for the
* next grant. Swept at release, and after any eviction while a slot is
* retiring (the release of its last message).
* The dead VM's conserved share (Q48_ONE) leaves with the slot; the donor
* gains free cells only, with no heat, so its own sum is unchanged.
*/
static uint32_t g_retiring_slots; /* atomic: slots waiting for step 2 */
static uint64_t g_slots_released; /* under g_lock_stadium */
uint64_t stadium_slots_released(void) { return g_slots_released; }
/* Step 1's eviction, `slot` locked. Returns the patrons left resident:
* pending messages, and anything eviction refuses (a patron holding
* another) -- the latter logged. A dead VM holds no pins that matter, so
* pins are cleared first. */
static size_t retire_evict_locked(int slot) {
size_t idx = stadium_quotas[slot].resident_head, left = 0;
while (idx != STADIUM_CELL_NONE) {
size_t next = link_to_size(stadium_resident_next[idx]);
StadiumPatronHeader *h = &stadium_cell_array[idx].header;
if (h->behaviour == (uint8_t)STADIUM_BEHAVIOUR_DELIVER) {
left++;
} else {
h->flags &= (uint8_t)~STADIUM_FLAG_PIN;
if (evict_locked(idx, (uint64_t *)0) != 0) {
left++;
log_message(LOG_ERROR, "Stadium: retiring slot %d: cell %zu can't be evicted (contains %u)",
slot, idx, (unsigned)h->contains);
}
}
idx = next;
}
return left;
}
void stadium_retire_sweep(void) {
size_t i;
if (!stadium_initialized || __atomic_load_n(&g_retiring_slots, __ATOMIC_ACQUIRE) == 0) return;
sk_spin_lock(&g_lock_stadium);
for (i = 0; i < stadium_max_vm_count_val; i++) {
int slot = (int)i, donor;
size_t head, count, idx, tail;
if (!stadium_quotas[slot].in_use || !stadium_quotas[slot].retiring) continue;
/* Detach its free list, if nothing is resident any more. */
slot_lock(slot);
if (stadium_quotas[slot].resident_head != STADIUM_CELL_NONE) {
slot_unlock(slot);
continue;
}
head = stadium_quotas[slot].free_head;
count = stadium_quotas[slot].free_count;
stadium_quotas[slot].free_head = STADIUM_CELL_NONE;
stadium_quotas[slot].free_count = 0;
stadium_words_slot_released(slot); /* its word row: slot lock held */
slot_unlock(slot);
/* The cells join the best donor (one slot lock at a time). */
donor = best_donor_slot(slot);
if (donor >= 0 && head != STADIUM_CELL_NONE) {
slot_lock(donor);
tail = STADIUM_CELL_NONE;
for (idx = head; idx != STADIUM_CELL_NONE; idx = link_to_size(stadium_cell_array[idx].header.link)) {
stadium_owner[idx] = (uint16_t)donor;
tail = idx;
}
stadium_cell_array[tail].header.link = size_to_link(stadium_quotas[donor].free_head);
stadium_quotas[donor].free_head = head;
stadium_quotas[donor].free_count += count;
slot_unlock(donor);
} else if (head != STADIUM_CELL_NONE) {
/* No other live slot (can't happen while Hera lives): keep them. */
slot_lock(slot);
stadium_quotas[slot].free_head = head;
stadium_quotas[slot].free_count = count;
slot_unlock(slot);
continue;
}
stadium_blocks_slot_released(slot); /* its block-table entries: global lock held */
stadium_quotas[slot].vm_id = vm_uuid_none();
stadium_quotas[slot].resident_head = STADIUM_CELL_NONE;
stadium_quotas[slot].reservoir = 0;
stadium_quotas[slot].consumed = 0;
stadium_quotas[slot].retiring = 0;
__atomic_store_n(&stadium_quotas[slot].in_use, 0, __ATOMIC_RELEASE);
__atomic_sub_fetch(&g_retiring_slots, 1, __ATOMIC_RELEASE);
g_slots_released++;
}
sk_spin_unlock(&g_lock_stadium);
}
int stadium_release_quota(VMUuid vm_id) {
int slot;
size_t left;
if (!stadium_initialized) return -1;
if (vm_uuid_is_hera(vm_id)) return -1; /* the root is never released */
sk_spin_lock(&g_lock_stadium);
slot = quota_slot_for_vm(vm_id);
if (slot < 0 || stadium_quotas[slot].retiring) {
sk_spin_unlock(&g_lock_stadium);
return -1;
}
slot_lock(slot);
left = retire_evict_locked(slot);
stadium_quotas[slot].retiring = 1;
slot_unlock(slot);
__atomic_add_fetch(&g_retiring_slots, 1, __ATOMIC_RELEASE);
sk_spin_unlock(&g_lock_stadium);
(void)left;
stadium_retire_sweep();
return 0;
}
/* Shared by stadium_cell_heat_get()/_set(): resident AND owned by vm_id's /* Shared by stadium_cell_heat_get()/_set(): resident AND owned by vm_id's
* own quota slot. Returns the quota slot on success, -1 on any refusal. */ * own quota slot. Returns the quota slot on success, -1 on any refusal. */
static int owned_resident_slot(VMUuid vm_id, size_t cell_index) { static int owned_resident_slot(VMUuid vm_id, size_t cell_index) {
+14
View File
@@ -303,6 +303,20 @@ static void block_dispatch_locked(int slot, uint32_t lbn, uint64_t heartbeat_tic
} }
} }
void stadium_blocks_slot_released(int slot) {
size_t i;
if (!blocks_initialized || slot < 0) return;
for (i = 0; i < block_slots_cap; i++) {
if (block_slots[i].state == BLOCK_SLOT_OCCUPIED && block_slots[i].quota_slot == (int16_t)slot) {
block_slots[i].state = BLOCK_SLOT_TOMBSTONE;
}
}
if ((size_t)slot < stadium_max_vm_count()) {
stat_promotions[slot] = 0;
stat_evictions[slot] = 0;
}
}
/* Freestanding: no libc printf. Same small utility stadium_words.c /* Freestanding: no libc printf. Same small utility stadium_words.c
* duplicates locally (and stadium.c before it) -- prints an unsigned * duplicates locally (and stadium.c before it) -- prints an unsigned
* decimal, no leading zeros. */ * decimal, no leading zeros. */
+9
View File
@@ -331,6 +331,15 @@ void stadium_word_forget(VMUuid vm_id, uint32_t word_id) {
stadium_slot_unlock(slot); stadium_slot_unlock(slot);
} }
void stadium_words_slot_released(int slot) {
if (!words_initialized || slot < 0 || (size_t)slot >= stadium_max_vm_count()) return;
if (word_slots[slot]) kfree(word_slots[slot]);
word_slots[slot] = (StadiumWordSlot *)0;
row_cap[slot] = 0;
stat_promotions[slot] = 0;
stat_evictions[slot] = 0;
}
void stadium_words_stats(VMUuid vm_id, uint64_t *promotions, uint64_t *evictions) { void stadium_words_stats(VMUuid vm_id, uint64_t *promotions, uint64_t *evictions) {
int slot = stadium_slot_lock_vm(vm_id); int slot = stadium_slot_lock_vm(vm_id);