cubelinux: cube(2) walks the store — CUBE_OP_ENUM and CUBE_OP_SPACES
Enumeration was the one operation the interface did not have, and the one a capability interface owes an explanation for: a listing is the opposite of "knowing a coordinate is the authorisation to use it". So the walk is bounded and explicit. A caller names the space, holds a cursor, and gets as many whole records as fit in the buffer it offered, packed `key(24) | value_len(u32) | value` in the store's own order. SPACES walks the distinct spaces that hold a record, one per call; range is that walk with the region as a filter, applied by the caller rather than by a second operation in the kernel. Its own argument block, versioned by its own size: SYSCALL_DEFINE2 peeks `size` and routes — 80 bytes is the coordinate block, 64 is this one. An interface that cannot grow has to be replaced, and this one grows by being given a new block. The end of a walk is the cursor alone. A batch holds as many whole records as fit, so it is FULL only when a record lands on the boundary and most batches come back short; a caller that reads "the buffer was not filled" as "the space is exhausted" truncates its listing to the first batch and cannot tell. ENUM answers a finished walk with no records and the cursor unmoved; SPACES answers it with -ENOENT, because the space after the last one is not there. That rule is in the uapi header because it is a contract detail, not an implementation one. The kernel caches no merged index: each call walks the records in order, asks the log — small, and the only thing that can override one — what the winner is, and skips what the cursor has already covered. O(records) a call, tens of milliseconds for the live store, which is worth more than a cache every write would have to invalidate.
This commit is contained in:
@@ -51,13 +51,20 @@ struct cube_args {
|
||||
* order — space first, then key — which is the order a checkpoint writes them and the order the
|
||||
* userspace store returns them, so a kernel listing and a userspace listing can be compared
|
||||
* directly. The space is not repeated per record: the caller named it.
|
||||
*
|
||||
* A walk ends when the cursor stops moving, and that is the only end signal: a batch holds as
|
||||
* many whole records as fit, so most batches come back short, and reading a short batch as the
|
||||
* end truncates a listing to its first batch. CUBE_OP_ENUM answers a finished walk with no
|
||||
* records and the cursor unchanged; CUBE_OP_SPACES answers it with -ENOENT, because asking for
|
||||
* the space after the last one is asking for a space that is not there. -ERANGE keeps its usual
|
||||
* meaning: not one whole record fits, and `len` says how much one needs.
|
||||
*/
|
||||
struct cube_enum_args {
|
||||
__u32 size; /* sizeof(struct cube_enum_args) as the caller built it */
|
||||
__u32 op; /* CUBE_OP_ENUM or CUBE_OP_SPACES */
|
||||
__u8 space[32]; /* in: the space to walk; out: the space found (CUBE_OP_SPACES) */
|
||||
__u64 cursor; /* in: 0 to start, or what the last call returned;
|
||||
* out: what to pass next
|
||||
* out: what to pass next — see the end-of-walk rule above
|
||||
*/
|
||||
__u64 value; /* user pointer: where to put the records */
|
||||
__u64 len; /* in: the buffer's capacity;
|
||||
|
||||
Reference in New Issue
Block a user