Files
LithosAnanake/v4/system/blocks.c
T
rajamesandClaude Opus 5.5 193523d873 fix(v4.0.0): three defects -- a refused device write, a failed starting state, a case that QUITs
A block write the device refuses after the kernel has taken the node's
copy: the node was told "Storage refused" while the kernel's cache kept
the new data, to be read back and perhaps written later.  The kernel now
puts its own copy back as it was.  Tested with a device whose writes can
be made to fail; it failed first.

POST's runner: a case whose starting state could not be set says so, with
what the node said, where it showed nothing; and a case that ends with
QUIT fails, as it did when the cases were a capsule, where the runner had
taken it for a completed line.  Both tests failed first.

The v4 boot says "Artemis: virtio-blk attached" only when the attach
worked.

make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and
riscv64 boot, POST 538 of 538, dict_hash 0x5f0a949a6fc8ef2b on all six:
logs/20261007-135741, -140004, -140329.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:05:14 -04:00

162 lines
5.8 KiB
C

/* blocks.c -- the kernel serves a node's block requests. See blocks.h.
*
* This is the one file of v4's that includes v3's headers: every block goes
* through v3's own calls.
*/
#include "v4/blocks.h"
#include "block_subsystem.h"
static void let_go(v4_blocks *w)
{
unsigned i;
for (i = 0; i < V4_BLOCK_SLOTS; i++) { w->block[i] = 0; w->changed[i] = 0; }
w->next = 0;
w->last = -1;
}
void v4_blocks_init(v4_blocks *w, v4_cell window)
{
w->window = window;
w->epoch = blk_subsys_epoch();
let_go(w);
}
static v4_cell slot_addr(const v4_blocks *w, unsigned s)
{
return w->window + (v4_cell)(s * V4_BLOCK_CELLS);
}
/* The block in slot s is written. Whether or not storage takes it, it is
* no longer marked. */
static int write_slot(v4_blocks *w, v4_node *n, unsigned s)
{
static uint8_t was[V4_BLOCK_CELLS * 4u]; /* the block as the kernel had it, in case it is refused */
uint8_t *b = blk_get_buffer(w->block[s], 1);
v4_cell at = slot_addr(w, s);
unsigned i;
w->changed[s] = 0;
if (!b) return V4_BLOCK_REFUSED;
for (i = 0; i < V4_BLOCK_CELLS * 4u; i++) was[i] = b[i];
for (i = 0; i < V4_BLOCK_CELLS; i++) {
v4_ucell c = (v4_ucell)n->mem[at + (v4_cell)i];
b[4u * i] = (uint8_t)(c & 0xFFu);
b[4u * i + 1u] = (uint8_t)((c >> 8) & 0xFFu);
b[4u * i + 2u] = (uint8_t)((c >> 16) & 0xFFu);
b[4u * i + 3u] = (uint8_t)((c >> 24) & 0xFFu);
}
if (blk_update(w->block[s]) == BLK_OK && blk_flush(w->block[s]) == BLK_OK) return V4_BLOCK_OK;
/* Storage would not have it. The node is told so, and the kernel does
* not keep what was refused: its own copy is put back as it was, so
* that the block reads as before and what was refused is not written
* later. The buffer is asked for again, since the failed write may
* have moved it. */
b = blk_get_buffer(w->block[s], 1);
if (b) {
for (i = 0; i < V4_BLOCK_CELLS * 4u; i++) b[i] = was[i];
(void)blk_update(w->block[s]);
}
return V4_BLOCK_REFUSED;
}
/* A slot to put a block in: an empty one, or one whose block is not marked,
* or, with every one marked, the next in turn, its block written first.
* *status is not V4_BLOCK_OK if that write was refused; the slot is empty
* all the same. */
static unsigned take_slot(v4_blocks *w, v4_node *n, int *status)
{
unsigned i, s;
*status = V4_BLOCK_OK;
for (i = 0; i < V4_BLOCK_SLOTS; i++) if (w->block[i] == 0) return i;
for (i = 0; i < V4_BLOCK_SLOTS; i++) {
s = (w->next + i) % V4_BLOCK_SLOTS;
if (!w->changed[s]) break;
}
if (i == V4_BLOCK_SLOTS) {
s = w->next;
*status = write_slot(w, n, s);
}
w->next = (s + 1u) % V4_BLOCK_SLOTS;
w->block[s] = 0;
w->changed[s] = 0;
if (w->last == (int)s) w->last = -1;
return s;
}
/* BLOCK and BUFFER: ( n -- waddr status ) */
static void slot_for(v4_blocks *w, v4_node *n, int read)
{
v4_cell num, at;
uint32_t block;
unsigned i, s;
int status;
if (n->ds.depth < 1u) { v4_dstack_reset(&n->ds); v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_REFUSED); return; }
num = v4_dstack_pop(&n->ds);
block = (uint32_t)num;
if (num < 1 || (uint64_t)num > 0xFFFFFFFFull || !blk_is_valid(block)) {
v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_RANGE);
return;
}
for (s = 0; s < V4_BLOCK_SLOTS && w->block[s] != block; s++) { }
if (s == V4_BLOCK_SLOTS) {
const uint8_t *b = 0;
s = take_slot(w, n, &status);
if (status == V4_BLOCK_OK && read) {
b = blk_get_buffer(block, 0);
if (!b) status = V4_BLOCK_REFUSED;
}
if (status != V4_BLOCK_OK) { v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, (v4_cell)status); return; }
at = slot_addr(w, s);
for (i = 0; i < V4_BLOCK_CELLS; i++)
n->mem[at + (v4_cell)i] = b ? (v4_cell)((v4_ucell)b[4u * i] | ((v4_ucell)b[4u * i + 1u] << 8)
| ((v4_ucell)b[4u * i + 2u] << 16) | ((v4_ucell)b[4u * i + 3u] << 24))
: 0;
w->block[s] = block;
w->changed[s] = 0;
}
w->last = (int)s;
v4_dstack_push(&n->ds, slot_addr(w, s));
v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_OK);
}
int v4_blocks_serve(v4_blocks *w, v4_node *n, v4_cell request)
{
unsigned s;
int status = V4_BLOCK_OK;
if (request > V4_REQ_BLOCK || request < V4_REQ_EMPTY_BUFFERS) return 0;
/* the chain is not what it was: what the slots hold is of no chain now */
if (blk_subsys_epoch() != w->epoch) { let_go(w); w->epoch = blk_subsys_epoch(); }
if (request == V4_REQ_EMPTY_BUFFERS) { let_go(w); return 1; }
/* the window is all in the node's memory, or nothing is done */
if (!v4_node_addr_ok(w->window) || !v4_node_addr_ok(w->window + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS - 1u))) {
if (request == V4_REQ_BLOCK || request == V4_REQ_BUFFER) {
if (n->ds.depth >= 1u) (void)v4_dstack_pop(&n->ds);
v4_dstack_push(&n->ds, 0);
}
v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_REFUSED);
return 1;
}
if (request == V4_REQ_BLOCK) { slot_for(w, n, 1); return 1; }
if (request == V4_REQ_BUFFER) { slot_for(w, n, 0); return 1; }
if (request == V4_REQ_UPDATE) {
if (w->last >= 0 && w->block[w->last] != 0) w->changed[w->last] = 1;
} else { /* SAVE-BUFFERS */
for (s = 0; s < V4_BLOCK_SLOTS; s++)
if (w->block[s] != 0 && w->changed[s] && write_slot(w, n, s) != V4_BLOCK_OK) {
status = V4_BLOCK_REFUSED;
w->block[s] = 0;
if (w->last == (int)s) w->last = -1;
}
}
v4_dstack_push(&n->ds, (v4_cell)status);
return 1;
}