The store's flag field is a substrate, and this is its read half: a v4 index entry carries a 16-bit class mask, and a scan by class is a walk that reads the mask and tests it. Nothing about the merge changes — the same log overlay, the same order — so a caller pays for its own class rather than for the store. The op takes a space, a mask, a mode (any/all), a cursor and a buffer, in its own size-versioned block. A mask of zero matches nothing, because naming no class is asking no question. Records come back as key | flags(2) | value_len | value: the mask travels, since a record can carry bits the scan did not name and no other op returns a mask. Both layouts the mask can be in are read. With the record still in the log it comes from the v2 log entry; after a fold it comes from the v4 index entry. The non-indexed path is not a corner — it is the state of every store between the write that classified something and the fold, so answering it with 'nothing' would make the substrate work only after a checkpoint. Also settles what an append writes into which log, since the entry's frame has to match the header a reader frames it by: a log that already holds v1 entries keeps taking v1 entries (a device folds first — that is the v4 migration — and a bare image keeps the log it has), an empty log is framed v2 on a device and left alone on a bare image, and a log with no header is framed v2 on a device and v1 on a bare image. The bare layout is the legacy one: it has no index for a mask to be folded into, so a mask written there could only be scanned and never checkpointed — half a feature, bought by making every existing reader of that layout grow a version it cannot use.
161 lines
8.0 KiB
C
161 lines
8.0 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.
|
|
*/
|
|
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 */
|
|
__u16 mask; /* in: the class mask to match; 0 matches nothing */
|
|
__u16 mode; /* in: CUBE_FLAG_ANY or CUBE_FLAG_ALL */
|
|
__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 */
|
|
|
|
#endif /* _UAPI_LINUX_CUBE_H */
|