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:
@@ -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)) => {
|
||||
|
||||
Reference in New Issue
Block a user