What is on a storage port (v4/system/storage.c): it takes Block read, Block write and Block data and answers Block data or Block done. It holds a view of a common chain, a private chain, or both; private block k is number 2^32 - 1 - k. Behind it is v3's block subsystem, reached through v4/system/store_v3.c, the one v4 file that includes v3's headers. v3's block code links here with its two device back ends, its log and its clock, and nothing else of v3. v4/tests/test_store.c: 36 checks at 64 bits, 35 at 32, and under the sanitizers. docs/v4.0.0/MESH.md 8.3, 8.4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
162 lines
5.6 KiB
C
162 lines
5.6 KiB
C
/* storage.c -- what is on a storage port. See storage.h.
|
|
* docs/v4.0.0/MESH.md section 8.
|
|
*
|
|
* Nothing here uses the C library: the bare-metal kernel links this file.
|
|
*/
|
|
#include "v4/storage.h"
|
|
|
|
void v4_store_init(v4_store *s, const v4_store_ops *ops, void *common, void *private_chain, v4_cell number)
|
|
{
|
|
unsigned i;
|
|
|
|
s->ops = ops;
|
|
s->common = common;
|
|
s->private_chain = private_chain;
|
|
s->number = number;
|
|
s->in.count = 0;
|
|
for (i = 0; i < V4_STORE_PENDING; i++) s->writing[i].used = 0;
|
|
s->head = 0;
|
|
s->count = 0;
|
|
s->out.count = 0;
|
|
s->out_at = 0;
|
|
s->out_ready = 0;
|
|
s->taken = 0;
|
|
}
|
|
|
|
/* The chain a number means and the block in it; or 0, with why in *status. */
|
|
static void *chain_for(const v4_store *s, uint64_t n, uint32_t *block, int *status)
|
|
{
|
|
*status = V4_STORE_RANGE;
|
|
if (n == 0 || n > V4_STORE_TOP) return 0;
|
|
if (s->private_chain) {
|
|
uint64_t k = V4_STORE_TOP - n, have = s->ops->blocks(s->private_chain);
|
|
if (have > V4_STORE_FIRST && k < have - V4_STORE_FIRST) { *block = (uint32_t)(V4_STORE_FIRST + k); return s->private_chain; }
|
|
}
|
|
*block = (uint32_t)n;
|
|
if (s->common) return s->common;
|
|
if (s->private_chain && n < V4_STORE_FIRST) return s->private_chain;
|
|
*status = V4_STORE_NONE;
|
|
return 0;
|
|
}
|
|
|
|
static void queue(v4_store *s, v4_cell to, int read, uint64_t block, int status)
|
|
{
|
|
unsigned at = (s->head + s->count) % V4_STORE_PENDING;
|
|
|
|
if (s->count == V4_STORE_PENDING) return; /* take does not let it come to this */
|
|
s->answer[at].to = to;
|
|
s->answer[at].read = read;
|
|
s->answer[at].block = block;
|
|
s->answer[at].status = status;
|
|
s->count++;
|
|
}
|
|
|
|
/* A block of data has come from `from`: it is written where that sender's
|
|
* BLOCK_WRITE said, if there was one. */
|
|
static int write_block(v4_store *s, v4_cell from)
|
|
{
|
|
unsigned i, k;
|
|
uint32_t block;
|
|
int status;
|
|
void *chain;
|
|
|
|
for (i = 0; i < V4_STORE_PENDING && !(s->writing[i].used && s->writing[i].from == from); i++) { }
|
|
if (i == V4_STORE_PENDING) return V4_STORE_REFUSED;
|
|
s->writing[i].used = 0;
|
|
if (v4_message_length(&s->in) != V4_STORE_BYTES) return V4_STORE_REFUSED;
|
|
chain = chain_for(s, s->writing[i].block, &block, &status);
|
|
if (!chain) return status;
|
|
for (k = 0; k < V4_STORE_BYTES; k++) s->bytes[k] = (unsigned char)v4_message_char(&s->in, k);
|
|
return s->ops->write(chain, block, s->bytes);
|
|
}
|
|
|
|
/* The message in s->in is whole. */
|
|
static void served(v4_store *s)
|
|
{
|
|
v4_cell from = v4_message_from(&s->in), type = v4_message_type(&s->in);
|
|
uint64_t block = (uint64_t)(v4_ucell)s->in.word[V4_MSG_HEADER];
|
|
unsigned i, free_at = V4_STORE_PENDING;
|
|
|
|
s->taken++;
|
|
if (v4_message_to(&s->in) != s->number) return;
|
|
if (type == V4_MSG_BLOCK_DATA) { queue(s, from, 0, 0, write_block(s, from)); return; }
|
|
if (type != V4_MSG_BLOCK_READ && type != V4_MSG_BLOCK_WRITE) return;
|
|
if (v4_message_length(&s->in) != 4) { queue(s, from, 0, 0, V4_STORE_REFUSED); return; }
|
|
if (type == V4_MSG_BLOCK_READ) { queue(s, from, 1, block, 0); return; }
|
|
|
|
/* a write: its number is kept until its block comes. A sender's second
|
|
* write before then takes the place of its first. */
|
|
for (i = 0; i < V4_STORE_PENDING; i++) {
|
|
if (s->writing[i].used && s->writing[i].from == from) { free_at = i; break; }
|
|
if (!s->writing[i].used && free_at == V4_STORE_PENDING) free_at = i;
|
|
}
|
|
if (free_at == V4_STORE_PENDING) { queue(s, from, 0, 0, V4_STORE_REFUSED); return; }
|
|
s->writing[free_at].used = 1;
|
|
s->writing[free_at].from = from;
|
|
s->writing[free_at].block = block;
|
|
}
|
|
|
|
int v4_store_take(void *self, v4_cell value)
|
|
{
|
|
v4_store *s = (v4_store *)self;
|
|
|
|
if (s->in.count == 0 && s->count == V4_STORE_PENDING) return 0;
|
|
if (v4_message_word(&s->in, value)) {
|
|
served(s);
|
|
s->in.count = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* The oldest answer waiting becomes the message in s->out. */
|
|
static void answer(v4_store *s)
|
|
{
|
|
v4_cell to = s->answer[s->head].to;
|
|
int status = s->answer[s->head].status;
|
|
unsigned i;
|
|
|
|
if (s->answer[s->head].read) {
|
|
uint32_t block;
|
|
void *chain = chain_for(s, s->answer[s->head].block, &block, &status);
|
|
if (chain) status = s->ops->read(chain, block, s->bytes);
|
|
} else if (status == V4_STORE_OK) {
|
|
status = -1; /* a write that worked: said below */
|
|
}
|
|
s->head = (s->head + 1) % V4_STORE_PENDING;
|
|
s->count--;
|
|
|
|
s->out.word[0] = to;
|
|
s->out.word[1] = s->number;
|
|
s->out.word[3] = 0;
|
|
s->out.word[4] = 0;
|
|
s->out.word[5] = 0;
|
|
if (status == V4_STORE_OK) { /* a read that worked: the block */
|
|
s->out.word[2] = V4_MSG_BLOCK_DATA;
|
|
s->out.word[6] = (v4_cell)V4_STORE_BYTES;
|
|
for (i = 0; i < V4_STORE_BYTES / 4u; i++)
|
|
s->out.word[V4_MSG_HEADER + i] = (v4_cell)((v4_ucell)s->bytes[4u * i] | (v4_ucell)s->bytes[4u * i + 1u] << 8
|
|
| (v4_ucell)s->bytes[4u * i + 2u] << 16 | (v4_ucell)s->bytes[4u * i + 3u] << 24);
|
|
s->out.count = V4_MSG_HEADER + V4_STORE_BYTES / 4u;
|
|
} else {
|
|
s->out.word[2] = V4_MSG_BLOCK_DONE;
|
|
s->out.word[6] = 4;
|
|
s->out.word[V4_MSG_HEADER] = status < 0 ? V4_STORE_OK : status;
|
|
s->out.count = V4_MSG_HEADER + 1u;
|
|
}
|
|
s->out_at = 0;
|
|
s->out_ready = 1;
|
|
}
|
|
|
|
int v4_store_give(void *self, v4_cell *value)
|
|
{
|
|
v4_store *s = (v4_store *)self;
|
|
|
|
if (!s->out_ready) {
|
|
if (s->count == 0) return 0;
|
|
answer(s);
|
|
}
|
|
*value = s->out.word[s->out_at++];
|
|
if (s->out_at == s->out.count) s->out_ready = 0;
|
|
return 1;
|
|
}
|