Files
cubelinux-kernel/include/uapi/linux/cube.h
T
surface-camera-build 71552fc157 cube(2): CUBE_OP_FLAG_SCAN takes a scope — one space, or every space
"Every error anywhere" and "every error here" are different questions, and
a space is a hard partition, so the scope is a field rather than a widening.
It is not a reserved space id because there is no such id to reserve: every
32-byte value is a legitimate space, root 0x00 and edge 0xFF…FF among them,
so a sentinel would be a space somebody could name. The field occupies what
was padding, which keeps sizeof unchanged — and that matters, because size is
what says which argument block arrived and cube_args is exactly eight bytes
wider.

An every-space answer carries each frame's space, and that is not decoration:
a walk's frame omits the space on the grounds that the caller named it, and
this caller named none. A coordinate is meaningless without its space, so an
answer that left it out would be unusable rather than merely terse. The space
leads because the (space, key) pair it forms is the order records are stored
in and returned in — so an every-space scan answers in exactly the order a
checkpoint writes.

The walk visits the space table's order and puts a space only the log writes
into in its place in that same order, which is the one thing a plain walk of
the table would miss entirely. A scope or mode this build does not know is
refused rather than defaulted: silently answering a narrower question than the
one asked is as quiet a way to be wrong as answering a wider one.
2026-09-22 00:31:56 -04:00

186 lines
9.6 KiB
C

/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* CUBELinux: the coordinate interface.
*
* The kernel's native interface to the store. Operations are the verbs the command language
* already defines — one language, and this is its kernel form (DESIGN-cube-interface.md).
*
* A coordinate is not a name and not a path: it is *where* a record is, and knowing it is the
* authorisation to use it. Nothing here resolves a name, and nothing enumerates.
*/
#ifndef _UAPI_LINUX_CUBE_H
#define _UAPI_LINUX_CUBE_H
#include <linux/types.h>
/* Which cube, and where in it. 32-byte space: unguessable, deliberately. */
struct cube_coord {
__u8 space[32];
__u64 x;
__u64 y;
__u64 z;
};
/*
* The argument block. `size` first, and checked: an interface that cannot grow is an
* interface that has to be replaced, and syscall numbers are permanent.
*/
struct cube_args {
__u32 size; /* sizeof(struct cube_args) as the caller built it */
__u32 op; /* CUBE_OP_* */
struct cube_coord coord; /* unused by CUBE_OP_SYNC */
__u64 value; /* user pointer: bytes to write, or where to put them */
__u64 len; /* in: bytes offered, or the buffer's capacity.
* out: on -ERANGE, the bytes that would be needed;
* on success for CUBE_OP_GET, the bytes read.
*/
};
#define CUBE_OP_PUT 1 /* store bytes at a coordinate */
#define CUBE_OP_GET 2 /* read them back */
#define CUBE_OP_DEL 3 /* remove the record */
#define CUBE_OP_SYNC 4 /* fold the log into the image */
#define CUBE_OP_ENUM 5 /* walk the records of a space, in batches */
#define CUBE_OP_SPACES 6 /* walk the spaces that hold records */
#define CUBE_OP_RANGE 7 /* walk the records of a space that lie in a box */
#define CUBE_OP_FLAG_SCAN 8 /* walk the records of a space whose class mask matches */
/*
* The walk's argument block: its own block rather than a wider `cube_args`, because it needs a
* cursor and a buffer, and the coordinate would otherwise be both an input and an output.
*
* Records are packed as `key(24) | value_len(u32, little-endian) | value`, in the store's own
* 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 — see the end-of-walk rule above
*/
__u64 value; /* user pointer: where to put the records */
__u64 len; /* in: the buffer's capacity;
* out: bytes written, or on -ERANGE what would be needed
*/
};
/*
* The region walk's argument block: its own block, for the walk's reason — it needs a box, a cursor
* and a buffer, and the coordinate would otherwise be both an input and an output. It is versioned
* by `size` like the other two, so this interface grows by gaining a block rather than by being
* replaced.
*
* A region is a box, inclusive on both corners. Records come back packed exactly as CUBE_OP_ENUM
* packs them — `key(24) | value_len(u32, little-endian) | value`, in the store's own order — so a
* kernel region answer and a userspace one can be compared byte for byte.
*
* The cursor is a COUNT OF RECORDS ALREADY RETURNED, and it is the end-of-walk signal for the same
* reason as the walk's: a batch holds as many whole records as fit, so most batches come back
* short, and reading a short batch as the end truncates the answer. The one difference is what it
* counts — a walk counts the records of the space, a region walk counts the records *in the box*,
* because those are the records it returns.
*
* Implementation, because it is what makes this cheap: **the kernel seeks.** The box's two corner
* keys bound every key inside it (`cube_format::key_span`), so a v3 image's sorted index is
* binary-searched for the foot of that span and read forward to its head. The span is a BOUND, not
* the set — records *outside* the box also have keys inside it (Z-order amplification) — so each
* candidate is decoded and tested against the box before it is returned. More records may therefore
* be examined than are returned, and that over-coverage is the honest cost of the span.
*/
struct cube_range_args {
__u32 size; /* sizeof(struct cube_range_args) as the caller built it */
__u32 op; /* CUBE_OP_RANGE */
__u8 space[32]; /* in: the space to search */
__u64 lo[3]; /* in: the region's near corner, inclusive */
__u64 hi[3]; /* in: the region's far corner, inclusive */
__u64 cursor; /* in: 0 to start, or what the last call returned;
* 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;
* out: bytes written, or on -ERANGE what would be needed
*/
};
/*
* The flag scan's argument block: `CUBE_OP_FLAG_SCAN`, the class-mask half of the store's
* classification substrate (DESIGN-flag-vocabularies.md). Its own block for the walks' reason — it
* needs a cursor and a buffer — and versioned by `size` like the other three.
*
* `mask` is a raw 16-bit class mask and this interface does not interpret a bit of it: the
* vocabulary that owns the bits (events today, sealing / lineage / lifecycle later) is the only
* thing that knows what they mean. `mode` says how to read the mask:
*
* CUBE_FLAG_ANY the record shares at least one bit with `mask` — "every error"
* CUBE_FLAG_ALL the record carries every bit of `mask` — "every Wi-Fi error"
*
* A `mask` of zero matches *nothing*, not everything: naming no class is asking no question, and a
* scan that answered a walk's worth of records to an empty question would be a walk wearing a
* scan's hat.
*
* The cursor counts **matches already returned**, as the region walk's counts records in its box,
* and for the same reason: the records examined before a match are not matches, so the index
* position of the cursor-th match is not arithmetic. A batch holds as many whole records as fit, so
* most batches come back short; reading a short batch as the end truncates the answer. A finished
* scan answers with no records and the cursor unchanged.
*
* Records come back as `key(24) | flags(2, little-endian) | value_len(u32, little-endian) | value`
* — the walk's frame with the class mask in it. The mask travels because a scan's answer has to say
* what class each record answered with: a record can carry bits beyond the one asked for, and no
* other operation returns a mask.
*
* Under CUBE_SPACE_EVERY the frame gains a leading `space(32)`, because it has to: a walk's frame
* leaves the space out on the grounds that the caller named it, and a caller who named no space
* cannot be told which one a record came from any other way. A coordinate is meaningless without
* its space, so an every-space answer that omitted it would be unusable rather than merely terse.
* The space leads because the (space, key) pair it forms is the order records are stored in and
* returned in.
*/
struct cube_flag_scan_args {
__u32 size; /* sizeof(struct cube_flag_scan_args) as the caller built it */
__u32 op; /* CUBE_OP_FLAG_SCAN */
__u8 space[32]; /* in: the space to scan, or ignored with CUBE_SPACE_EVERY */
__u16 mask; /* in: the class mask to match; 0 matches nothing */
__u16 mode; /* in: CUBE_FLAG_ANY or CUBE_FLAG_ALL */
__u32 every_space; /* in: CUBE_SPACE_ONE or CUBE_SPACE_EVERY — see below */
__u64 cursor; /* in: 0 to start, or what the last call returned;
* out: what to pass next — see the end-of-scan rule above
*/
__u64 value; /* user pointer: where to put the records */
__u64 len; /* in: the buffer's capacity;
* out: bytes written, or on -ERANGE what would be needed
*/
};
#define CUBE_FLAG_ANY 0 /* the record shares at least one bit with the mask */
#define CUBE_FLAG_ALL 1 /* the record carries every bit of the mask */
/*
* The scan's scope. A space is a hard partition, so this is a choice between two different
* questions and never a filter that can be widened by accident:
*
* CUBE_SPACE_ONE the records of `space` — "this class here"
* CUBE_SPACE_EVERY the records of every space — "this class anywhere"
*
* It is a field and not a reserved space id because there is no such id to reserve: every 32-byte
* value is a legitimate space (root `0x00` and edge `0xFF…FF` are both in use), so a sentinel would
* be a space somebody could name. The field occupies what was padding, so `sizeof` is unchanged —
* which matters, because `size` is what says which argument block arrived and `cube_args` is
* exactly eight bytes wider. A caller that zeroes its block (and every caller does) gets
* CUBE_SPACE_ONE, which is the narrower question.
*/
#define CUBE_SPACE_ONE 0 /* scan only `space` */
#define CUBE_SPACE_EVERY 1 /* scan every space */
#endif /* _UAPI_LINUX_CUBE_H */