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>
86 lines
4.0 KiB
C
86 lines
4.0 KiB
C
/* blocks.h -- a node asks its kernel for a block.
|
|
* docs/v4.0.0/MESH.md section 8, docs/v4.0.0/ENGINE.md 3.3,
|
|
* docs/v4.0.0/V3-PARITY.md 1d.
|
|
*
|
|
* A node only ever asks for a block by number, and it asks the kernel: a
|
|
* request written to the port where its kernel is, as any kernel word is.
|
|
* This is v3's way (v3/src/word_source/block_words.c). The node has a
|
|
* WINDOW in its memory, V4_BLOCK_SLOTS slots of 256 cells, as a v3 VM has
|
|
* four slots of 1 KiB at the top of its memory. The kernel keeps the
|
|
* record of which block is in which slot and does all the copying, and it
|
|
* writes into the window and nowhere else in the node. The requests, the
|
|
* same for every node:
|
|
*
|
|
* V4_REQ_BLOCK ( n -- waddr status ) the slot that holds block n, read
|
|
* from storage if it is not in one
|
|
* V4_REQ_BUFFER ( n -- waddr status ) a slot for block n, of zeros if it
|
|
* is not in one: nothing is read
|
|
* V4_REQ_UPDATE ( -- status ) the block most recently asked for
|
|
* is marked changed
|
|
* V4_REQ_SAVE_BUFFERS ( -- status ) every block marked is written
|
|
* V4_REQ_EMPTY_BUFFERS ( -- ) every slot is let go; nothing is
|
|
* written
|
|
*
|
|
* Their numbers are below zero: the requests a host names for its own
|
|
* words (KERNEL-WORD) count up from 1.
|
|
*
|
|
* waddr is the word address of the slot, or 0 when the status is not 0.
|
|
* The status is 0; or V4_BLOCK_RANGE, there is no such block; or
|
|
* V4_BLOCK_REFUSED: storage would not have a block that had to be written,
|
|
* the stack did not hold the request's argument, or the window is not all
|
|
* in the node's memory.
|
|
*
|
|
* WHEN A BLOCK IS WRITTEN is FORTH-79's: UPDATE marks it, and it is written
|
|
* by SAVE-BUFFERS or when its slot is wanted for another block. (v3's
|
|
* UPDATE copies the slot to the kernel at once; the standard is followed.)
|
|
* A slot whose block is not marked is taken before one whose block is. A
|
|
* block that storage would not take is let go of: its slot is empty, and
|
|
* the kernel keeps the block as it was before, not what was refused.
|
|
*
|
|
* When the kernel's chain of devices changes, everything in the slots is
|
|
* let go and nothing of it is written, as v3 does (blk_vm_check_epoch).
|
|
*
|
|
* A block is 1024 bytes and 256 cells, four bytes to a cell, the first
|
|
* lowest, whatever the cell width: a block read leaves the rest of each
|
|
* cell zero, and a write takes the low 32 bits.
|
|
*
|
|
* The blocks are the kernel's block subsystem's (v3/src/block_subsystem.c).
|
|
* Who may have which block is not checked here yet (MESH.md 8.4).
|
|
*/
|
|
#ifndef V4_BLOCKS_H
|
|
#define V4_BLOCKS_H
|
|
|
|
#include "v4/node.h"
|
|
|
|
#define V4_REQ_BLOCK (-1)
|
|
#define V4_REQ_BUFFER (-2)
|
|
#define V4_REQ_UPDATE (-3)
|
|
#define V4_REQ_SAVE_BUFFERS (-4)
|
|
#define V4_REQ_EMPTY_BUFFERS (-5)
|
|
|
|
#define V4_BLOCK_OK 0
|
|
#define V4_BLOCK_REFUSED 2
|
|
#define V4_BLOCK_RANGE 3
|
|
|
|
#define V4_BLOCK_SLOTS 4u
|
|
|
|
/* What the kernel keeps for one node. */
|
|
typedef struct {
|
|
v4_cell window; /* word address of the first slot */
|
|
uint32_t block[V4_BLOCK_SLOTS]; /* the block in each slot; 0: none */
|
|
unsigned char changed[V4_BLOCK_SLOTS]; /* UPDATEd and not yet written */
|
|
unsigned next; /* where the search for a slot to take begins */
|
|
int last; /* the slot most recently asked for; -1: none */
|
|
uint64_t epoch; /* the chain as it was when the slots were filled */
|
|
} v4_blocks;
|
|
|
|
/* A node's window is at `window`, and nothing is in it. */
|
|
void v4_blocks_init(v4_blocks *w, v4_cell window);
|
|
|
|
/* If `request` is one of the five, serve it for the node and return 1.
|
|
* Otherwise touch nothing and return 0. The node is stopped, blocked at
|
|
* its port, when this is called. */
|
|
int v4_blocks_serve(v4_blocks *w, v4_node *n, v4_cell request);
|
|
|
|
#endif /* V4_BLOCKS_H */
|