cubelinux: the release starts with a digit, because module tooling demands it
The store's sort no longer lives on the kernel stack (heapsort, see the parent commit's gate), and the release is now 6.19.3-cubelinux0.6 instead of CUBELinux.0.6 — depmod/mkinitramfs reject a version whose first character is not numeric, so the literal name cannot be uname -r. The box's own kernel uses the same shape (6.19.3-cube+); this is the same concession.
This commit is contained in:
@@ -9,7 +9,12 @@ NAME = CUBELinux
|
||||
# release. The base version stays in VERSION/PATCHLEVEL/SUBLEVEL above (visible in
|
||||
# `make kernelversion`), while `uname -r` and /lib/modules report the CUBELinux
|
||||
# release, with any -dirty or SCM suffix still appended by setlocalversion.
|
||||
CUBELINUX_VERSION = CUBELinux.0.6
|
||||
#
|
||||
# The release must start with a digit: `depmod`, `mkinitramfs` and the rest of the module tooling
|
||||
# reject a version whose first character is not numeric, so a release spelled `CUBELinux.0.6` is
|
||||
# one the machine cannot load modules for. The box's own kernel works around this the same way
|
||||
# (`6.19.3-cube+`), so CUBELinux does too: the Linux base, then `-cubelinux`, then our version.
|
||||
CUBELINUX_VERSION = 6.19.3-cubelinux0.6
|
||||
|
||||
# *DOCUMENTATION*
|
||||
# To see a list of typical targets execute "make help"
|
||||
|
||||
@@ -598,8 +598,37 @@ impl Merged {
|
||||
|
||||
/// Put the entries in the order a checkpoint would write them: by space, then by key,
|
||||
/// with the later write of a coordinate last — so a group's final entry decides it.
|
||||
///
|
||||
/// This is a heapsort, and the reason is a panic rather than a preference.
|
||||
/// `slice::sort_unstable` allocates about 3.5 KiB of *kernel stack* per recursion level in
|
||||
/// this kernel's Rust, so a store with a couple of hundred records overflowed the 16 KiB
|
||||
/// kernel stack and took the machine down — `BUG: TASK stack guard page was hit`, with the
|
||||
/// instruction pointer inside
|
||||
/// `core::slice::sort::unstable::quicksort::<cubelinux_store::Entry>` and `sub rsp, 0xdd8` in
|
||||
/// the function's first bytes. A heapsort has a constant frame and allocates nothing, so the
|
||||
/// size of a store cannot decide whether reading it is safe, and the sort cannot fail for want
|
||||
/// of memory in the middle of a read.
|
||||
///
|
||||
/// It is not stable, which costs nothing here: `seq` is part of the ordering key, so the order
|
||||
/// is total and two entries never compare equal.
|
||||
fn sort_entries(&mut self) {
|
||||
self.entries.as_mut_slice().sort_unstable();
|
||||
let entries = self.entries.as_mut_slice();
|
||||
let len = entries.len();
|
||||
if len < 2 {
|
||||
return;
|
||||
}
|
||||
// Build a max-heap, then repeatedly move the maximum to the end.
|
||||
let mut start = len / 2;
|
||||
while start > 0 {
|
||||
start -= 1;
|
||||
sift_down(entries, start, len);
|
||||
}
|
||||
let mut end = len;
|
||||
while end > 1 {
|
||||
end -= 1;
|
||||
entries.swap(0, end);
|
||||
sift_down(entries, 0, end);
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&self, e: &Entry) -> &[u8] {
|
||||
@@ -608,6 +637,29 @@ impl Merged {
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore the heap property below `root` over `entries[..end]`.
|
||||
///
|
||||
/// Iterative on purpose: the whole point of the heapsort above is that nothing here grows with the
|
||||
/// size of the store.
|
||||
fn sift_down(entries: &mut [Entry], mut root: usize, end: usize) {
|
||||
loop {
|
||||
let left = 2 * root + 1;
|
||||
if left >= end {
|
||||
return;
|
||||
}
|
||||
let mut largest = left;
|
||||
if left + 1 < end && entries[left] < entries[left + 1] {
|
||||
largest = left + 1;
|
||||
}
|
||||
if entries[root] < entries[largest] {
|
||||
entries.swap(root, largest);
|
||||
root = largest;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the log that follows the image, if there is one, and apply its entries.
|
||||
///
|
||||
/// The log is a delta on the image, so its entries override image records for the same
|
||||
|
||||
@@ -40,5 +40,29 @@ struct cube_args {
|
||||
#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 */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
__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
|
||||
*/
|
||||
};
|
||||
|
||||
#endif /* _UAPI_LINUX_CUBE_H */
|
||||
|
||||
Reference in New Issue
Block a user