Skip to content

revert(minimald): restore the 8 MiB guest vsock receive window - #922

Merged
twitchyliquid64 merged 1 commit into
mainfrom
fix/restore-8mib-vsock-window
Jul 22, 2026
Merged

revert(minimald): restore the 8 MiB guest vsock receive window#922
twitchyliquid64 merged 1 commit into
mainfrom
fix/restore-8mib-vsock-window

Conversation

@norrietaylor

@norrietaylor norrietaylor commented Jul 22, 2026

Copy link
Copy Markdown
Member

Reverts #916, which reverted #885. Restores the 8 MiB guest vsock receive window.

Why it should go back

#885 was reverted before the libkrun fix had been tested in production. That test has now run, and the libkrun fix is not sufficient on its own.

unstable (6d239bc) carries the fill loop from #884 and still fails. Tom hit it, and it reproduces here on a stack verified identical to his — his on-disk libkrun.1.dylib is byte-identical to the published versions/6d239bc1/libkrun-macos-arm64.dylib (sha256 4d1056da…), and disassembly confirms the fill loop is present in it.

The fill loop stops us fragmenting the stream when libkrun outruns the writer inside one wakeup. It does nothing when the writer is slower across wakeups: each notification finds a few hundred bytes, the loop drains them, hits EAGAIN, and emits a packet a fraction of the descriptor's size. An instrumented guest kernel attributed every reset to the skb-depth branch, rx_qlen exactly 455, packets of 100–452 bytes.

What the window buys

0 of 6 failures at 8 MiB against 6 of 6 on the same machine at the 256 KiB default.

buf_alloc bounds queue depth at buf_alloc / SKB_TRUESIZE(0). At 256 KiB that is 455 packets — the exact rx_qlen in every reject record. At 8 MiB it is 14,563.

This is headroom, not immunity. It does not change the ratio between the byte ceiling and the skb ceiling; it keeps a normal upload out of the fragmented tail where the reset happens. It belongs alongside a fix for the fragmentation, not instead of one.

Implementation note worth keeping

SO_VM_SOCKETS_BUFFER_MAX_SIZE must be raised before SO_VM_SOCKETS_BUFFER_SIZE. vsock_update_buffer_size() clamps the latter to the former and both default to 256 KiB, so the obvious ordering appears to succeed and changes nothing. The restored code does them in the right order and reads back the applied value.

Related work in flight

  • #921 — libkrun patch to coalesce on EAGAIN rather than emit a sub-skb packet. 16/16 against 0/10 for the current dylib.
  • A client-side fix under measurement: the small writes originate with us, since russh turns each SSH packet into its own write() on the unix socket and UDS does no coalescing. Buffering there removes the cause rather than compensating for it in the VMM.

Draft until we settle which of the three lands first and in what combination.

Refs: #869

Note

Restore 8 MiB guest vsock receive window in minimald

Reintroduces the AF_VSOCK receive window configuration that was previously removed. On Linux, after binding the VsockListener, the daemon calls the new set_vsock_rx_window helper to set SO_VM_SOCKETS_BUFFER_MAX_SIZE and SO_VM_SOCKETS_BUFFER_SIZE to 8 MiB, then reads back the effective size and logs whether the request succeeded, was partially applied, or failed.

Macroscope summarized cdaf024.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Linux vsock listener buffering to better support large uploads.
    • Automatically requests an 8 MiB receive window and reports when the system applies a smaller limit.
    • Added diagnostic logging when the receive window cannot be increased, helping explain potential upload failures.

Reverts commit b0f07a2 (PR #916), which reverted 4823653 (PR #885).

#885 was reverted before the libkrun fix had been tested in production.
That test has now run and the fix is not sufficient on its own: the
shipped 6d239bc build carries the fill loop and still fails, because
the loop exits on EAGAIN with a partly filled descriptor whenever the
writer is slower than libkrun's reader.

The window is the one measure with an unambiguous result behind it --
0 of 6 failures at 8 MiB against 6 of 6 on the same machine at the
256 KiB default. It raises the skb-depth ceiling from 455 queued
packets to 14,563, which is the bound every observed reset has hit.

This is headroom, not immunity: it does not change the ratio between
the two ceilings, it keeps a normal upload out of the fragmented tail
where the reset happens. It belongs alongside a libkrun fix rather
than instead of one.

Note `SO_VM_SOCKETS_BUFFER_MAX_SIZE` must be raised before
`SO_VM_SOCKETS_BUFFER_SIZE`: vsock_update_buffer_size() clamps the
latter to the former and both default to 256 KiB, so the obvious
ordering silently changes nothing.

Refs: #869
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Linux AF_VSOCK startup now requests an 8 MiB receive window, reads back the effective size, and logs whether the kernel applied, clamped, or rejected the configuration.

Changes

AF_VSOCK receive window

Layer / File(s) Summary
Receive-window configuration
crates/minimald/src/main.rs
Adds the Linux-only 8 MiB setting and helper that configures AF_VSOCK socket buffers and returns the effective receive-window size.
Listener startup integration
crates/minimald/src/main.rs
Configures the bound vsock listener before READY and logs debug or warning messages for the resulting window state.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: twitchyliquid64

Poem

I’m a rabbit with ears held high,
Watching the vsock buffers fly.
Eight MiB set with careful care,
The kernel tells us what is there.
Before READY hops along,
Logs keep every window song.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change, but it does not follow the required Summary/Testing/Checklist template or include testing evidence. Add explicit Summary, Testing, and Checklist sections, including test commands/output and whether docs were updated and a BREAKING CHANGE footer is needed.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is a clear Conventional Commit-style summary of restoring the 8 MiB vsock receive window.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@norrietaylor
norrietaylor marked this pull request as ready for review July 22, 2026 21:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/minimald/src/main.rs`:
- Around line 739-760: Gate the vsock listener branch in async_main, including
VsockListener, VSOCK_RX_WINDOW_BYTES, and set_vsock_rx_window usage, behind
#[cfg(target_os = "linux")] or a Linux-only helper so non-Linux builds do not
type-check it. Add the std::mem::size_of import required by set_vsock_rx_window.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 38dcbfa6-ccb7-4e92-983c-87ae3f0cc607

📥 Commits

Reviewing files that changed from the base of the PR and between 159f883 and cdaf024.

📒 Files selected for processing (1)
  • crates/minimald/src/main.rs

Comment on lines +739 to +760

// Widen the receive window before anything connects: `__vsock_create`
// copies `buffer_size` from the listening socket onto every socket it
// accepts, so this one call covers every session. Best effort — a
// daemon with the default window is the old, buggy behaviour, not a
// reason to refuse to boot.
match set_vsock_rx_window(&listener, VSOCK_RX_WINDOW_BYTES) {
Ok(effective) if effective >= VSOCK_RX_WINDOW_BYTES => {
tracing::debug!(bytes = effective, "raised the vsock receive window");
}
Ok(effective) => tracing::warn!(
requested = VSOCK_RX_WINDOW_BYTES,
effective,
"vsock receive window clamped below the requested size; large uploads may still \
fail with ENOBUFS"
),
Err(e) => tracing::warn!(
error = %e,
"could not raise the vsock receive window; large uploads may fail with ENOBUFS"
),
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1. Check for cfg(target_os = "linux") near the vsock branch/import in main.rs
rg -n -B5 -A5 'VsockListener::bind|use tokio_vsock' crates/minimald/src/main.rs

# 2. Check for an existing size_of import in the file
rg -n 'use std::mem::(size_of|\*)|size_of::<u64>' crates/minimald/src/main.rs

# 3. Check Cargo.toml for minimald for target-specific deps/features
fd Cargo.toml crates/minimald --exec cat {}

# 4. Check declared Rust version/edition (rust-toolchain, workspace Cargo.toml)
fd rust-toolchain -H
fd Cargo.toml --max-depth 1 --exec grep -n 'edition\|rust-version' {}

Repository: gominimal/minimal

Length of output: 4695


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- crates/minimald/src/main.rs: top imports and linux-gated items ---'
sed -n '1,120p' crates/minimald/src/main.rs

echo
echo '--- crates/minimald/src/main.rs: vsock branch around call site ---'
sed -n '720,770p' crates/minimald/src/main.rs

echo
echo '--- crates/minimald/src/main.rs: helper definition ---'
sed -n '790,870p' crates/minimald/src/main.rs

echo
echo '--- rust-toolchain.toml ---'
cat rust-toolchain.toml

echo
echo '--- workspace Cargo.toml edition/rust-version ---'
grep -n '^\s*\(edition\|rust-version\)\s*=' Cargo.toml || true

Repository: gominimal/minimal

Length of output: 11210


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- size_of imports/usages in crates/minimald/src/main.rs ---'
rg -n 'size_of|mem::size_of|use .*size_of' crates/minimald/src/main.rs

echo
echo '--- cfg gates around the async entry/body ---'
rg -n -B3 -A8 '#\[cfg\(target_os = "linux"\)\]|async fn|fn async_main|match .*Command::Run|VsockListener::bind' crates/minimald/src/main.rs

Repository: gominimal/minimal

Length of output: 5423


Gate the Linux-only vsock path and fix the missing import

  • async_main still type-checks this branch on non-Linux targets, but it references Linux-only VsockListener, VSOCK_RX_WINDOW_BYTES, and set_vsock_rx_window; wrap the whole vsock branch in #[cfg(target_os = "linux")] or move it behind a Linux-only helper.
  • set_vsock_rx_window also calls size_of::<u64>() without importing std::mem::size_of, so it will not compile as written.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/minimald/src/main.rs` around lines 739 - 760, Gate the vsock listener
branch in async_main, including VsockListener, VSOCK_RX_WINDOW_BYTES, and
set_vsock_rx_window usage, behind #[cfg(target_os = "linux")] or a Linux-only
helper so non-Linux builds do not type-check it. Add the std::mem::size_of
import required by set_vsock_rx_window.

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.

2 participants