Files
LithosAnanke/v4/system/blocks.c
T
Claude 3bd81097e1 chore(license): an SPDX header on every source file that had none (LicenseRef-Starship-1.0) [skip ci]
The header scan (Captain Bob, 2026-11-10): 788 source files -- 351 with the
SL-1.0 header, 12 with the old name (docs/working/experiments, kept by
ruling), 31 under someone else's licence (tools/kconfig, Linux's, GPL-2.0:
kept), and 394 with none. Ruled: the SPDX form; capsules (.4th, .f18) and
proofs (.thy) left for a ruling of their own (mkcapsule's 64x16 block format,
content-addressed capsules).

253 files now begin
    SPDX-License-Identifier: LicenseRef-Starship-1.0
    Copyright (c) 2023-2026 Robert A. James
as a /* */ block (C, headers, assembly, linker scripts) or # lines (scripts,
Makefiles, Kconfig; below a shebang or a coding line); tools/mkcapsule.c,
which had the copyright line, gets the SPDX line only.

Not touched: docs/working/ (archive and old campaign material, as the
ruling kept the experiments); v4/system/post_cases.c (generated by
v4/tools/mkpost.py, "do not edit" -- the generator has the header).

Comments only: the kernel builds on amd64, aarch64, riscv64; make -C v4
test-32: 39 programs, 0 failures; every changed script parses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-10 04:12:49 +00:00

165 lines
5.9 KiB
C

/* SPDX-License-Identifier: LicenseRef-Starship-1.0
* Copyright (c) 2023-2026 Robert A. James
*/
/* 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;
}