/* 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) { 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; 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); } return (blk_update(w->block[s]) == BLK_OK && blk_flush(w->block[s]) == BLK_OK) ? V4_BLOCK_OK : 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; }