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>
This commit is contained in:
rajames
2026-10-07 14:05:14 -04:00
co-authored by Claude Opus 5.5
parent bc6deab3f5
commit 193523d873
11 changed files with 585 additions and 13 deletions
+43
View File
@@ -52,6 +52,25 @@ static v4_cell tell(v4_cell request)
static int in_window(v4_cell a) { return a >= WINDOW && a < WINDOW + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS) && (a - WINDOW) % (v4_cell)V4_BLOCK_CELLS == 0; }
static unsigned char *chain_block(unsigned num) { return num < 2048u ? ram + ((size_t)num + BLK_FORTH_SYS_RESERVED) * 1024u : dev + (size_t)(num - 2048u) * 1024u; }
/* A device whose writes can be made to fail: 2048 blocks of RAM behind v3's
* device interface (v3/include/blkio.h). */
#define FAILING_BLOCKS 2048u
static unsigned char failing_bytes[FAILING_BLOCKS * 1024u];
static int failing;
static int f_open(struct blkio_dev *d, const blkio_params_t *p) { (void)d; (void)p; return BLKIO_OK; }
static int f_close(struct blkio_dev *d) { (void)d; return BLKIO_OK; }
static int f_read(struct blkio_dev *d, uint32_t b, void *dst) { (void)d; memcpy(dst, failing_bytes + (size_t)b * 1024u, 1024); return BLKIO_OK; }
static int f_write(struct blkio_dev *d, uint32_t b, const void *src)
{
(void)d;
if (failing) return BLKIO_EIO;
memcpy(failing_bytes + (size_t)b * 1024u, src, 1024);
return BLKIO_OK;
}
static int f_flush(struct blkio_dev *d) { (void)d; return failing ? BLKIO_EIO : BLKIO_OK; }
static int f_info(struct blkio_dev *d, blkio_info_t *out) { (void)d; memset(out, 0, sizeof *out); return BLKIO_OK; }
static const blkio_vtable_t failing_vt = { f_open, f_close, f_read, f_write, f_flush, f_info };
int main(void)
{
unsigned i;
@@ -165,6 +184,30 @@ int main(void)
CHECK(ask(V4_REQ_BLOCK, 2070) == V4_BLOCK_OK && (n.mem[got_addr] & 0xFF) == 'N', "and the block is read again");
}
/* ---- a write that fails on the device leaves the block as it was ---- */
{
static blkio_dev_t fdev;
blkio_params_t params;
uint32_t first = 0, n_blocks = 0;
params.forth_block_size = 0; params.total_blocks = FAILING_BLOCKS; params.opaque = 0;
CHECK(blkio_open(&fdev, &failing_vt, &params) == BLKIO_OK && blk_subsys_attach_device(&fdev) == BLK_OK
&& blk_get_device_range(&fdev, &first, &n_blocks) == BLK_OK && n_blocks > 8 && blk_subsys_confirm_format(first) == BLK_OK,
"a disk joins the chain and is formatted: %u blocks from %u", n_blocks, first);
CHECK(ask(V4_REQ_BLOCK, (v4_cell)first + 3) == V4_BLOCK_OK, "one of its blocks is asked for");
for (i = 0; i < V4_BLOCK_CELLS; i++) n.mem[got_addr + (v4_cell)i] = (v4_cell)0x4F4F4F4F;
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK, "and written: all O");
CHECK(ask(V4_REQ_BLOCK, (v4_cell)first + 3) == V4_BLOCK_OK, "it is asked for again");
for (i = 0; i < V4_BLOCK_CELLS; i++) n.mem[got_addr + (v4_cell)i] = (v4_cell)0x4E4E4E4E;
failing = 1;
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_REFUSED, "with the device failing, a write of all N is refused");
failing = 0;
(void)tell(V4_REQ_EMPTY_BUFFERS);
CHECK(ask(V4_REQ_BLOCK, (v4_cell)first + 3) == V4_BLOCK_OK && n.mem[got_addr] == (v4_cell)0x4F4F4F4F && n.mem[got_addr + 255] == (v4_cell)0x4F4F4F4F,
"and the block is what it was before: the kernel does not keep what the device refused: %lx", (unsigned long)n.mem[got_addr]);
CHECK(blk_flush(0) == BLK_OK && ask(V4_REQ_BLOCK, (v4_cell)first + 3) == V4_BLOCK_OK && n.mem[got_addr] == (v4_cell)0x4F4F4F4F,
"nor does it write it later, when the device works again");
}
/* ---- what is not a block request ---- */
v4_dstack_reset(&n.ds);
v4_dstack_push(&n.ds, 11); v4_dstack_push(&n.ds, 22);