Skip to content

Add 9p backends: diod over TCP + QEMU virtio-9p via virtme-ng (vagrant/QEMU stack) - #7

Open
yarikoptic-gitmate wants to merge 6 commits into
masterfrom
claude/9p-mount-planning-0c3nqc
Open

yarikoptic-gitmate wants to merge 6 commits into
masterfrom
claude/9p-mount-planning-0c3nqc

Conversation

@yarikoptic-gitmate

@yarikoptic-gitmate yarikoptic-gitmate commented Aug 29, 2026

Copy link
Copy Markdown

Adds 9p — the filesystem behind Vagrant/libvirt type: "9p" synced folders, WSL2 drive shares, and QEMU -virtfs generally — to the matrix, as two backends plus their CI rows, docs, and provisioning.

What's in here

Design doc, reviewed (drafts/9p-backend-plan.md): the original plan was adversarially reviewed by two independent passes (systems mechanics; harness fit). The doc is now the decision record: what was decided, what the reviews corrected (several load-bearing facts), and what is deferred.

bin/eval-under-9p-tcp — diod (LLNL's 9P2000.L server, Ubuntu universe) exports a fresh dir on 127.0.0.1; the kernel v9fs client mounts it. Architecturally the NFS backend's sibling; no VM. diod runs unprivileged in its documented single-user mode by default (root only for mount/umount); --run-as-root switches to the multi-user export — the 9p analog of --no-root-squash, wired to target_needs_root().

bin/eval-under-9p-virtio — the real vagrant/QEMU stack: QEMU's virtfs server exporting into a virtme-ng guest booted from the host's own rootfs, with the suite running inside the guest (host-installed targets exist there unchanged; exit status and stdio return over virtio-serial). Highlights:

  • Guest kernel pinned for CI (refs: 9p-kernel: v6.8, Ubuntu mainline build) because the v9fs client is the kernel and its semantics moved in 6.4/6.8; the backend itself never reads matrix.yamlrun-under.sh hands the pin over, and the local default is the host kernel (offline-friendly).
  • Needs no host root for the mapped row; --shell drops into a live guest with the mount up for morning-after-a-red-cell debugging.
  • --disable-microvm (vng's microvm has no PCI bus stock guest kernels can see), host-side --vm-timeout (the in-guest per-target timeout can't catch a boot hang), guest log teed to /var/log/eval-under-9p-virtio.log (uploaded as an artifact), and --copy-out ferrying suite outputs (git's t/test-results/**) out of the guest through the 9p export itself — vng's --rwdir shares turned out not to be uid-faithful (measured; documented in GOTCHAS).

Matrix/CI: rows 9p tcp (diod) and 9p virtio (mapped) (28 cells total), on ubuntu-24.04 via a new optional per-backend runs-on key (default stays ubuntu-22.04, BeeGFS rows untouched); install_9p_tcp/install_9p_virtio (KVM udev rule + a pre-warm boot moving the ~180 MB pinned-kernel download outside the suite timeout); dump-failure-logs.sh cases; GOTCHAS settings tables and known-red entries; README grid + docs regenerated; Vagrant VM provisioning for all new deps.

Matrix outcome (full run on this branch)

The 20 pre-existing cells match master's baseline exactly — 10 green, 10 documented known-red; no regressions from the shared-file changes (4-field backend tuple, per-row runs-on, install-backend now sourcing matrix.sh).

All 8 new 9p cells ran their suites end-to-end; every red is a documented finding (GOTCHAS "Known-red cells"), the highlights being:

  • git-annex test hangs identically on both 9p servers: testremote type git.init fails ("Not initialized"), then unavailable remote / removeKey blocks until the per-target timeout — the pre-registered locking suspect (diod's docs themselves warn record locking "will deadlock").
  • stress-ng isolates the open-but-unlinked file gap (fstat/ftruncate → ENOENT), with a clean server divergence: fallocate fails on diod only. The pinned v6.8 guest kernel logs WARNs in v9fs_fid_lookup_with_uid while these run.
  • pjdfstest (238 files, ~8800 assertions) completes on both rows; diod's divergence concentrates in the long-pathname 03.t scripts, the open/06.t flags matrix, and unlink/14.t (the same assertion the NFS row flags).
  • git testsuite: 194 (diod) vs 177 (virtio) failed assertions; t1050-large diod-only; t1517-outside-repo/t0450 shared and green on the ext4 control row.

Three harness bugs were caught and fixed along the way (each reproduced locally before the fix was pushed): the diod-log chown vs fs.protected_regular on runners; vng --rwdir uid-unfaithfulness (replaced by --copy-out); and an ambiguous exit-124 message.

Validation done before each push

End-to-end local runs of eval-under-9p-virtio (TCG boot of the pinned v6.8 mainline kernel, virtfs device, mount + probe, quoting round-trip through vng's layers, copy-out with a dropped-user write); shellcheck bin/ci/*.sh bin/eval-under*; matrix-json.sh / gen-readme-matrix.sh --check; codespell; reuse lint; run-under.sh translation exercised for all four new paths.

Deferred variants (passthrough rows including vagrant-libvirt's true default of passthrough-under-unprivileged-QEMU, cache=loose, virtiofs sibling) are recorded in GOTCHAS "Not yet covered" and reachable locally via flags today.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MovQRK32XDaU4PRDrCP6S7

claude added 2 commits August 29, 2026 17:12
Design notes for adding 9p as a matrix row: run the suite inside a
virtme-ng guest against a QEMU -virtfs export (the actual
vagrant-libvirt synced-folder stack), with a diod-over-TCP localhost
variant mirroring the NFS backend where host kernels allow it.
Covers backend-script knobs, matrix/CI wiring (per-row runs-on,
pinned guest kernel), provisioning, a Phase-0 feasibility probe for
the open runner questions (/dev/kvm, 9p modules on azure kernels),
and PR sequencing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MovQRK32XDaU4PRDrCP6S7
Two new backends put 9p -- the filesystem behind Vagrant/libvirt "9p"
synced folders, WSL2 drive shares, and QEMU -virtfs in general -- into
the matrix, per the reviewed design in drafts/9p-backend-plan.md:

- bin/eval-under-9p-tcp: diod (9P2000.L) exporting a fresh dir on
  127.0.0.1, kernel v9fs mount -- the NFS backend's architectural
  sibling; no VM. diod runs unprivileged in its single-user mode by
  default; --run-as-root switches to the multi-user export for the
  privileged suites (the 9p analog of --no-root-squash).

- bin/eval-under-9p-virtio: QEMU's virtfs server (-virtfs local,...)
  into a virtme-ng guest booted from the host's own rootfs, suite runs
  inside the guest; exit status and stdio come back over virtio-serial.
  Guest kernel pinned in CI (refs: 9p-kernel, handed over by
  run-under.sh -- the backend itself never reads matrix.yaml and
  defaults to the host kernel for local runs). Needs no host root for
  the mapped-xattr row. --shell drops into a live guest for debugging.

Wiring: two matrix rows (9p-tcp/n/a, 9p-virtio/mapped) on
ubuntu-24.04 via a new optional per-backend runs-on key (default
ubuntu-22.04 keeps BeeGFS rows untouched); install_9p_tcp/_9p_virtio
in install-backend.sh (incl. the KVM udev rule and a pre-warm boot
that moves the ~180 MB pinned-kernel download outside the suite
timeout); 9p cases in run-under.sh (needs-root wiring, VM timeout,
--share-rw for git's test-results) and dump-failure-logs.sh; guest log
teed to /var/log/eval-under-9p-virtio.log and added to the artifact
list; GOTCHAS gains both settings tables and the semantics that make
red 9p cells readable (mapped-xattr absorbs privileged ops; QEMU
answers every TLOCK with success while diod takes whole-file flocks;
diod caps msize at 64 KiB); provision installs the new deps.

Validated end to end in a container: TCG boot of the pinned v6.8
mainline kernel, virtfs device visible (--disable-microvm), 9p mount +
usability probe, argument fidelity through vng's double quoting layer
(printf %q + a dq-escape pass), env trio on the mount, exit-status
propagation. shellcheck-clean; README grid regenerated (28 cells);
REUSE and codespell clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MovQRK32XDaU4PRDrCP6S7
@yarikoptic-gitmate yarikoptic-gitmate changed the title Plan: add 9p backend for VM-shared-filesystem testing Add 9p backends: diod over TCP + QEMU virtio-9p via virtme-ng (vagrant/QEMU stack) Aug 29, 2026
claude added 4 commits August 29, 2026 19:58
On the hosted runners (fs.protected_regular=2) the sequence root-mktemp
-> chown-to-invoker -> root '>>' append in sticky /tmp is denied, so
diod never started and every 9p-tcp cell failed with "diod did not
bind". The chown was never needed: the current shell opens the log fd
and diod only inherits it, whatever uid diod runs as. Reproduced and
verified both ways by flipping fs.protected_regular locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MovQRK32XDaU4PRDrCP6S7
The 9p-virtio/git cell died in check-chainlint: vng's --rwdir shares
are not uid-faithful (measured on the runner stack and reproduced
locally) -- writes arrive host-side as the sharing daemon's identity,
so a non-root guest process gets EACCES inside a directory it just
created. Let the suite's in-tree writes (chainlinttmp, test-results)
go to the guest's uid-faithful tmpfs overlay instead, and add
--copy-out: stage2 ferries the requested guest paths back through the
9p export after the command (failed runs included), and the host half
restores them to their real paths with ownership normalized to the
invoker, so the artifact upload can read them. run-under.sh now passes
--copy-out for the git target's t/test-results; --share-rw stays as a
power tool with the caveat documented in its usage and in GOTCHAS.
Verified end to end locally: a dropped-user guest write into the
overlay lands back on the host with content intact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MovQRK32XDaU4PRDrCP6S7
Both the wrapped command's own in-guest timeout and the host-side
--vm-timeout surface as exit 124; the first CI git-annex hang showed
the message wrongly asserting the VM was killed when the guest had in
fact reported cleanly (its dmesg tail was right there above). Say both
possibilities and how to tell them apart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MovQRK32XDaU4PRDrCP6S7
All eight 9p cells ran their suites end-to-end on the PR's matrix run;
every red is a finding, not a harness failure, and the legacy twenty
cells match master's baseline exactly. Recorded per house rule:

- git-annex test hangs identically on both servers (testremote init
  fails, then unavailable-remote/removeKey blocks until the timeout) --
  the pre-registered locking suspect;
- stress-ng: fstat/ftruncate ENOENT on open-but-unlinked files;
  fallocate fails only on diod (server divergence), with v6.8 guest
  kernel WARNs in the v9fs fid-lookup path on the QEMU row;
- pjdfstest: long-pathname 03.t scripts, the open/06.t flags matrix,
  and unlink/14.t (shared with the NFS row) on diod; virtio tally in
  the artifact;
- git testsuite: 194 (diod) / 177 (virtio) failed assertions,
  t1050-large diod-only, t1517/t0450 shared and green on ext4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MovQRK32XDaU4PRDrCP6S7
@yarikoptic

Copy link
Copy Markdown
Member

apparently 9p is "no good" for sqlite3 even!

(SQLite3 returned ErrorIO while attempting to perform prepare "PRAGMA journal_mode=WAL;": disk I/O error)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants