/* 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 /* 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. */ __u16 flags; /* in: CUBE_OP_PUT only — the class mask to stamp on the * record. The mask is written where the record is written, * which is the moment its class is known for certain; a * reader never has to re-derive it. 0 is "no class", which * is what every record written before the field existed * reads as, so a caller that does not classify is not * writing a special value. * * A store whose image is the legacy packed layout has no * field to put a mask in, and drops it: that layout cannot * carry a class and saying otherwise would be a lie about * the bytes on disk. */ __u16 reserved; /* must be 0 */ }; #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 */