store: the kernel captures its own events, as classed records
Every part of this was in place except the point of it: a record can carry a 16-bit class,
CUBE_OP_PUT stamps one and CUBE_OP_FLAG_SCAN retrieves by class, the vocabulary is a userspace
convention, and the kernel already stamped its own boot record with the boot class. What was
missing was a kernel that captures *events* — one that writes about what happened to it rather
than only what a caller asked for. Until now the kernel's account of saying no was a line in
dmesg, which is not somewhere a later reader can ask.
Three events, in the events space (0xFB), each classed by the vocabulary it belongs to:
bad-op ERROR a request the interface does not offer, refused and recorded
clock-set BOOT the epoch was provisional, and the record was rewritten
boot-record-late ERROR|BOOT the record only landed on a retry — the one that matters most,
because that defect's effect was invisible in the store
The write is bounded twice (EVENT_BUDGET, 32 a boot; REFUSAL_BUDGET, 8 among them) and that is
not tidiness: a kernel that appends a record per event can turn a storm of refused requests into
a storm of writes, which this store has already met from the other side when a walk with no store
behind it served ~200,000 invented records a minute. Past the ceiling the kernel logs and stops.
The refusal capture is exported for the syscall layer to call (`cubelinux_kernel_capture_refusal`)
because that is where the refusals that leave the store usable happen — a bad size, an op that
does not exist, a value past the maximum. A store that cannot be read at all is the one refusal
the kernel cannot record into itself, and that is stated rather than papered over.
This commit is contained in:
@@ -34,6 +34,13 @@ int cubelinux_kernel_put(const __u8 *space, __u64 x, __u64 y, __u64 z,
|
||||
ssize_t cubelinux_kernel_get(const __u8 *space, __u64 x, __u64 y, __u64 z,
|
||||
void *buf, size_t len, __u16 *out_flags);
|
||||
int cubelinux_kernel_del(const __u8 *space, __u64 x, __u64 y, __u64 z);
|
||||
/*
|
||||
* Capture a request this layer refused, as a classed record in the events space. The refusals worth
|
||||
* recording are the ones that leave the store usable — a caller asking for something the interface
|
||||
* does not offer — because those are the moments the machine said no and carried on. It takes the
|
||||
* driver's write lock itself, so it must be called from here and not from inside an op.
|
||||
*/
|
||||
int cubelinux_kernel_capture_refusal(__u64 op, int errno, const __u8 *kind, size_t kind_len);
|
||||
int cubelinux_kernel_sync(void);
|
||||
|
||||
/*
|
||||
@@ -156,8 +163,10 @@ static long cube_args_op(unsigned int op, void __user *uargs)
|
||||
* The size is the caller's, and it must be the one this kernel implements: a caller
|
||||
* built against a later block would otherwise have fields silently ignored.
|
||||
*/
|
||||
if (args.size != sizeof(struct cube_args))
|
||||
if (args.size != sizeof(struct cube_args)) {
|
||||
cubelinux_kernel_capture_refusal(op, EINVAL, "bad-size", 8);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case CUBE_OP_PUT:
|
||||
@@ -167,12 +176,15 @@ static long cube_args_op(unsigned int op, void __user *uargs)
|
||||
case CUBE_OP_SYNC:
|
||||
break;
|
||||
default:
|
||||
cubelinux_kernel_capture_refusal(op, EINVAL, "bad-op", 6);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (op == CUBE_OP_PUT || op == CUBE_OP_GET) {
|
||||
if (args.len > CUBE_MAX_VALUE)
|
||||
if (args.len > CUBE_MAX_VALUE) {
|
||||
cubelinux_kernel_capture_refusal(op, E2BIG, "too-big", 7);
|
||||
return -E2BIG;
|
||||
}
|
||||
if (args.len > 0) {
|
||||
buf = kvmalloc(args.len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
|
||||
Reference in New Issue
Block a user