read: take the log window instead of copying it, and name the boot class bit

A coordinate read cost 0.082 ms in a 500-record store and 0.081 ms in a 20,000-record
one — flat to a microsecond while the index search does nine probe reads against
fifteen. So the device reads are nearly free and the per-call cost is SETUP, and the
clearest piece of setup was a copy of the log window into a second buffer on every read.
`v3_get` copied it because what the log says borrows it while reading the image needs
`&mut image`; that borrow is one field, so moving the field out settles it for nothing.

Measured on #82: get 0.082 -> 0.078 ms, spaces 0.339 -> 0.267 ms. That is small in the
GUEST, and the guest cannot show the real number: its store carries a log of a few
hundred bytes, while the box's log is 132,679 bytes — so the copy this removes costs
~130 KB of memcpy and a ~130 KB allocation per read there, and only the box can measure
it.

Also names the store's own class bit. `BOOT_FLAGS` was a bare `1 << 7` and read as
`TYPE_MASK == 10` ("data type: code") to anything applying `WordFlags` — a latent
collision, not a live one, and the resolution is a declaration rather than a move:
`WordFlags` is a different field and its sixteen bits are all allocated, so there is no
bit to borrow, and the space axis already makes a boot record findable (reserved space
0xFC) without a class bit at all. What it needed was a vocabulary that claims it, which
this comment now does. The one bit the two fields share is shared on purpose and by name:
0x0800, `SEALED_FLAG` in `cube-store-seal`, which `WordFlags` calls `ENCRYPTED` — same
meaning in both places, which is the pattern rather than the exception.
This commit is contained in:
surface-camera-build
2026-09-23 17:53:38 -04:00
parent e968b3964e
commit aab316a9a1
+19 -6
View File
@@ -278,6 +278,18 @@ const BOOT_SPACE: [u8; SPACE_ID_LEN] = [0xFC; SPACE_ID_LEN];
/// The class mask the boot record stamps: `EventFlags::BOOT`, bit 7. The kernel cannot link
/// `cube-core`, so the bit is spelled out here and on the userspace side, and the agreement is held
/// by `verify-boot-record` — the same shape as the `0xFC` space id, for the same reason.
/// The store class for "the kernel wrote this about its own boot" — the first bit of the store's
/// own class vocabulary.
///
/// The class field is `v4`'s two bytes in the index entry, and it is **its own 16-bit space**. The
/// reason this is named here instead of borrowed from `cube_core::WordFlags` is that `WordFlags` is a
/// different field whose sixteen bits are all spoken for: reading this bit through it gives
/// `TYPE_MASK == 10`, "data type: code", which is a meaning this bit does not have. A vocabulary
/// belongs to one field, and this field's first vocabulary is `events`.
///
/// `0x0800` is the one bit this field and `WordFlags` share, deliberately and by name: the sealing
/// layer stamps it here (`SEALED_FLAG` in `cube-store-seal`) and it means encrypted in both places,
/// so the two agree instead of colliding. See `DESIGN-flag-vocabularies.md` §6.
const BOOT_FLAGS: u16 = 1 << 7;
/// The coordinate the record lives at. One record, the current boot: each boot overwrites the last,
@@ -3089,12 +3101,13 @@ unsafe fn v3_get(
len: usize,
out_flags: *mut u16,
) -> isize {
// The log is owned for the duration: what it says borrows it, and reading the image needs
// `&mut image`. It is what a checkpoint has not folded, so it is small.
let mut log = KVVec::<u8>::new();
if log.extend_from_slice(image.log(), GFP_KERNEL).is_err() {
return -12; // -ENOMEM
}
// The log is *taken*, not copied. It used to be copied into a second buffer because what it says
// borrows it while reading the image needs `&mut image` — but that borrow is one field, and moving
// the field out settles it for nothing. Measured, a coordinate read costs the same in a store
// forty times larger while the index search does nine probe reads against fifteen: the device
// reads are nearly free and the per-call cost is this kind of setup, so a copy of the log window
// on every read is the thing worth removing, not the reads.
let log = core::mem::replace(&mut image.log, KVVec::new());
match log_effect(log.as_slice(), &sp, &key) {
Some(Effect::Delete) => return -2, // -ENOENT
Some(Effect::Write(value, flags)) => {