Shell store probe backoff, and the IPC round trip in bench #11

Open
nonos-sync wants to merge 7 commits from nonos-sync/gh-477 into desktop-ui-and-measurement AGit
Member

Stacked on #476. Two commits, both from measuring the boot rather than reading it.

Shell store probe

vfs_pool spends its first seconds staging packages off the block device, where it answers nobody. The store health probe ran on every clock tick through that window, and each miss cost a full IPC timeout inside the shell's own loop: around 40 dead calls per boot with the thing drawing the desktop stalled behind each one.

Backs off 250ms doubling to a 4s ceiling. Nothing acts on the answer but a toast, and the store will not decode differently for being asked twice as often.

Found by counting [DIAG] ipc.call unanswered vfs_pool in a boot log: 39 from desktop_shell, 8 from the installer, 1 from the text editor.

IPC round trip in bench

The probes added in #476 only needed the kernel, so they measured syscall entry and exit and a trip through the scheduler. The operation every capsule pays for is a call to another capsule and the reply coming back, which needs a peer that answers.

attest is that peer. Its healthcheck reads no state and touches no device, so what gets timed is the transport rather than work at the far end. A service that did anything would report its own cost as ours.

256 samples rather than 2048, since each one wakes another process and comes back. A profile with no attest capsule reports the peer as unregistered rather than a measurement of zero.

Not included

The remaining vfs_pool timeouts come from desktop::refresh and packages::refresh, which list a directory every clock tick for the life of the session. That is 1/s rather than per frame, so it is defensible, but a desktop should learn about file changes from a notification rather than by asking. Left alone here because it needs a change notification path, not a backoff.


Opened on GitHub by eKisNonos as pull request 477. Review and merge happen there while this repository is kept in step from GitHub; this copy follows it, and is marked merged or closed when it is.

Stacked on [#476](https://github.com/NON-OS/nonos-micro-kernel/issues/476). Two commits, both from measuring the boot rather than reading it. ### Shell store probe `vfs_pool` spends its first seconds staging packages off the block device, where it answers nobody. The store health probe ran on every clock tick through that window, and each miss cost a full IPC timeout inside the shell's own loop: around 40 dead calls per boot with the thing drawing the desktop stalled behind each one. Backs off 250ms doubling to a 4s ceiling. Nothing acts on the answer but a toast, and the store will not decode differently for being asked twice as often. Found by counting `[DIAG] ipc.call unanswered vfs_pool` in a boot log: 39 from `desktop_shell`, 8 from the installer, 1 from the text editor. ### IPC round trip in bench The probes added in [#476](https://github.com/NON-OS/nonos-micro-kernel/issues/476) only needed the kernel, so they measured syscall entry and exit and a trip through the scheduler. The operation every capsule pays for is a call to another capsule and the reply coming back, which needs a peer that answers. `attest` is that peer. Its healthcheck reads no state and touches no device, so what gets timed is the transport rather than work at the far end. A service that did anything would report its own cost as ours. 256 samples rather than 2048, since each one wakes another process and comes back. A profile with no attest capsule reports the peer as unregistered rather than a measurement of zero. ### Not included The remaining `vfs_pool` timeouts come from `desktop::refresh` and `packages::refresh`, which list a directory every clock tick for the life of the session. That is 1/s rather than per frame, so it is defensible, but a desktop should learn about file changes from a notification rather than by asking. Left alone here because it needs a change notification path, not a backoff. --- Opened on GitHub by eKisNonos as [pull request 477](https://github.com/NON-OS/nonos-micro-kernel/pull/477). Review and merge happen there while this repository is kept in step from GitHub; this copy follows it, and is marked merged or closed when it is.
vfs_pool spends its first seconds staging packages off the block device,
where it answers nobody. The store health probe ran on every clock tick
through that window and each miss cost a full IPC timeout inside the
shell's own loop, around forty dead calls per boot with the desktop
stalled behind each one.

Doubles from 250ms to a 4s ceiling. Nothing acts on the answer but a
toast, and the store will not decode differently for being asked twice as
often.
The probes so far only needed the kernel, so they measured syscall entry
and exit and a trip through the scheduler. The operation every capsule
actually pays for is a call to another capsule and the reply coming back,
and it needs a peer that answers.

attest is that peer. Its healthcheck reads no state and touches no
device, so what gets timed is the transport rather than work at the far
end: a service that did anything would report its own cost as ours.

256 samples rather than 2048, since each one wakes another process and
comes back, and a few hundred already puts a real value at the 99th
percentile.

A profile with no attest capsule says the peer is not registered rather
than reporting a measurement of zero.
The desktop icons and the package list each ran a full directory listing
on every clock tick, for the life of the session, and threw away an
identical answer nearly every time. A listing walks the tree, serialises
every entry and copies it across an IPC boundary. Once a second, to learn
nothing.

vfs_pool keeps a counter that moves when anything mutates and answers
OP_GENERATION with it. The shell reads eight bytes, and pays for a
listing only when the number changed.

Bumped in dispatch rather than in each handler, and on the attempt rather
than on success. A refused mkdir moving the counter costs one redundant
listing; a handler that forgets to bump leaves a desktop that never
updates, and those two mistakes are not the same size.

Reads do not bump it. OP_SEEK and OP_OPEN move file state that no listing
shows, so counting them would wake every watcher on a read-only load.

No answer counts as changed. vfs_pool is unreachable while it stages
packages, and treating silence as nothing-new would hold a stale desktop
for as long as the service stayed quiet.

This is not a notification. That needs subscriber state and outbound
sends inside a service every capsule depends on, and this is the cheap
two thirds of it.
Measured the previous commit rather than assuming it: timed out vfs_pool
calls went from 46 a boot to 50.

The counter read is still an IPC call. While vfs_pool stages packages it
answers nobody, so that call times out exactly as the listing it replaced
did, and returning changed on no answer sent the caller on to two more
listings that were about to time out for the same reason. One dead call
per tick became three.

No answer now means no change: the desktop holds what it has, which is
what it would have drawn anyway, and asks again on a widening gap.

The two backoffs in this capsule made the same mistake independently, so
there is one now. It resets on an answer, so a service that goes quiet
again is noticed promptly rather than at whatever ceiling it last
reached.
The seeder's comment said a staging attempt costs up to one blk timeout.
It is one per chunk. blk::load read the whole container in a single call:
a capacity query, the header, the table of contents, then every payload
walked in whole-sector chunks because they run to hundreds of kilobytes
against a driver that caps a request, plus a digest over each. Hundreds
of block round trips with the receive loop suspended for all of them.

That is the reason every caller in the system grew a backoff today. The
desktop shell logged forty unanswered calls a boot, the installer eight,
and the text editor died on its one because it never got the frame that
builds its window. All of it downstream of a service that stops
answering for seconds at boot.

The load is resumable now and runs on an 8ms budget: read a chunk, check
the clock, hand the receive loop back. The longest anyone waits is a
single block request, and an idle machine still stages at close to device
speed rather than one chunk per poll.

Removed the whole-store path rather than keeping both. Two loaders that
verify digests separately is how one ends up trusting bytes the other
would refuse; finish_entry is now the single place a payload is checked.
The bug that kept recurring today has one shape: a capability that is
implemented, sits in the dispatch table, and nothing calls. MkCapsuleVerify,
MkAttestDoc and MkFutexWait were all dark at once, and a filesystem mount
had never been invoked. No compiler warns about it and no test runs it.

check_syscall_reach.py lists every kernel tag no file under userland names,
as tag4(b"XXXX") or as the little-endian hex literal libc writes for one
of them. The count is a baseline that may only go down. Ten today, each
explained in the notes file beside it. Three of those are capability grant,
revoke and check, which no capsule can reach.

The std PAL lives in the pinned rust-src outside this tree and is not
scanned; the notes say which entries it accounts for.

Checked that it fires: dropping the futex wait tag from libc takes the
count to eleven and the gate refuses it.
Every syscall this kernel dispatches is checked against the caller's mask,
and the three calls that ask about, extend and withdraw that mask were
implemented and reachable from no capsule. A program learned it lacked a
capability by being refused, which is the wrong time to find out.

mk_cap_check is ungated, which discloses nothing the process table does
not already publish to everyone. mk_cap_grant and mk_cap_revoke require
Admin, which only init holds, so from any shipped capsule they refuse:
the right answer, and now an answer rather than an absence.

The Verify screen's claim that this window holds exactly what its
manifest declares rested on one kernel path, a scan of the process table.
It now needs two to agree, the table and MkCapCheck over the same bits.
One path can be wrong on its own; two disagreeing is a kernel fault
surfacing where a person can see it.

Unreachable syscall baseline 10 to 7.
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin +refs/pull/11/head:nonos-sync/gh-477
git switch nonos-sync/gh-477

Merge

Merge the changes and update on Forgejo.
git switch desktop-ui-and-measurement
git merge --no-ff nonos-sync/gh-477
git switch nonos-sync/gh-477
git rebase desktop-ui-and-measurement
git switch desktop-ui-and-measurement
git merge --ff-only nonos-sync/gh-477
git switch nonos-sync/gh-477
git rebase desktop-ui-and-measurement
git switch desktop-ui-and-measurement
git merge --no-ff nonos-sync/gh-477
git switch desktop-ui-and-measurement
git merge --squash nonos-sync/gh-477
git switch desktop-ui-and-measurement
git merge --ff-only nonos-sync/gh-477
git switch desktop-ui-and-measurement
git merge nonos-sync/gh-477
git push origin desktop-ui-and-measurement
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
NON-OS/nonos-micro-kernel!11
No description provided.