From 18f973a327e93c250b543818b79415098442a218 Mon Sep 17 00:00:00 2001 From: Stephen Akinyemi Date: Sat, 18 Apr 2026 23:40:58 +0100 Subject: [PATCH] feat(krun): make virtio-vsock opt-in when TSI does not need it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously Vm::build() unconditionally attached a virtio-vsock device via configure_vsock(). That consumed a permanent IRQ + MMIO slot even for VMs that never use vsock — typically ones with virtio-net and more than one virtio-fs tag, where neither HIJACK_INET nor HIJACK_UNIX applies and the vsock has no actual work to do. Skip the attach in that case and let callers opt in when they need guest-visible vsock sockets for their own purposes. - src/krun/src/api/vm.rs: configure_vsock() now early-returns when the caller hasn't opted in (VmResources::request_vsock) and no TSI flag is needed. builder.rs (VMM side) already only injects tsi_hijack / tsi_hijack_unix into the kernel cmdline inside `if let Some(vsock) = vm_resources.vsock.get()`, so the guest never gets told to expect a transport that isn't there. - src/krun/src/api/builders.rs + builder.rs: add MachineBuilder::vsock(bool), threaded through to vmr.request_vsock. - src/vmm/src/resources.rs: add request_vsock: bool (default false) alongside the existing split_irqchip flag. Guest is safe with the device absent: the krun-init shipped in libkrunfw is built without __TIMESYNC__, so it has no AF_VSOCK callers; and Linux's CONFIG_VIRTIO_VSOCKETS driver sits dormant when no virtio-vsock device is enumerated. Even with TIMESYNC enabled, socket(AF_VSOCK, ...) returning ENODEV is handled gracefully in clock_worker(). Net effect for microsandbox's typical VM: baseline virtio-mmio IRQ use drops by one (from 8 to 7 on x86_64), freeing an extra slot for user-defined mounts. --- src/krun/src/api/builder.rs | 1 + src/krun/src/api/builders.rs | 13 +++++++++++++ src/krun/src/api/vm.rs | 12 +++++++++++- src/vmm/src/resources.rs | 6 ++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/krun/src/api/builder.rs b/src/krun/src/api/builder.rs index 6a3319a41..8567b8a72 100644 --- a/src/krun/src/api/builder.rs +++ b/src/krun/src/api/builder.rs @@ -333,6 +333,7 @@ impl VmBuilder { .map_err(|err| map_vm_config_error(&self.machine, err))?; vmr.nested_enabled = self.machine.nested_virt; vmr.split_irqchip = self.machine.split_irqchip; + vmr.request_vsock = self.machine.vsock; // Apply filesystem configuration #[cfg(not(feature = "tee"))] diff --git a/src/krun/src/api/builders.rs b/src/krun/src/api/builders.rs index 49315fecc..ad00904d1 100644 --- a/src/krun/src/api/builders.rs +++ b/src/krun/src/api/builders.rs @@ -43,6 +43,7 @@ pub struct MachineBuilder { pub(crate) hyperthreading: bool, pub(crate) nested_virt: bool, pub(crate) split_irqchip: bool, + pub(crate) vsock: bool, } //-------------------------------------------------------------------------------------------------- @@ -298,6 +299,7 @@ impl MachineBuilder { hyperthreading: false, nested_virt: false, split_irqchip: false, + vsock: false, } } @@ -337,6 +339,17 @@ impl MachineBuilder { self.split_irqchip = enabled; self } + + /// Force-attach a virtio-vsock device to the guest. + /// + /// By default, vsock is only attached when needed as a TSI transport + /// (no virtio-net → HIJACK_INET, or single root virtio-fs on Linux → + /// HIJACK_UNIX). Set this to `true` when the guest needs a vsock for + /// its own purposes even though TSI would not otherwise require one. + pub fn vsock(mut self, enabled: bool) -> Self { + self.vsock = enabled; + self + } } impl Default for MachineBuilder { diff --git a/src/krun/src/api/vm.rs b/src/krun/src/api/vm.rs index 874f32d42..fee208e0d 100644 --- a/src/krun/src/api/vm.rs +++ b/src/krun/src/api/vm.rs @@ -256,7 +256,13 @@ impl Vm { Ok(()) } - /// Configure vsock device. + /// Configure the vsock device. + /// + /// The device is only attached when actually needed — either because the + /// caller explicitly requested it (`VmBuilder::vsock(true)`), or because + /// TSI needs it as a transport (no virtio-net → HIJACK_INET; single root + /// virtio-fs on Linux → HIJACK_UNIX). This keeps the per-VM IRQ/MMIO + /// budget free when nothing actually uses vsock. fn configure_vsock(&mut self) -> Result<()> { use devices::virtio::TsiFlags; @@ -279,6 +285,10 @@ impl Vm { tsi_flags = self.maybe_enable_hijack_unix(tsi_flags); } + if !self.vmr.request_vsock && tsi_flags.is_empty() { + return Ok(()); + } + let vsock_config = VsockDeviceConfig { vsock_id: "vsock0".to_string(), guest_cid: 3, diff --git a/src/vmm/src/resources.rs b/src/vmm/src/resources.rs index d49518f20..005706dae 100644 --- a/src/vmm/src/resources.rs +++ b/src/vmm/src/resources.rs @@ -189,6 +189,11 @@ pub struct VmResources { pub nested_enabled: bool, /// Whether to enable split irqchip pub split_irqchip: bool, + /// Force-enable the virtio-vsock device even when no TSI transport is + /// required. When `false`, vsock is only attached if `configure_vsock` + /// determines it is needed (HIJACK_INET when there's no virtio-net, or + /// HIJACK_UNIX when there's a single root virtio-fs on Linux). + pub request_vsock: bool, /// Do not create an implicit console device in the guest pub disable_implicit_console: bool, /// The console id to use for console= in the kernel cmdline @@ -437,6 +442,7 @@ mod tests { smbios_oem_strings: None, nested_enabled: false, split_irqchip: false, + request_vsock: false, disable_implicit_console: false, serial_consoles: Vec::new(), virtio_consoles: Vec::new(),