From e5f8b82eab8a1d1d4ef8c39d2d936fb34038adf4 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Sat, 13 Jun 2026 06:59:03 -0700 Subject: [PATCH] Force virtiofs devices to a single virtio queue (vcpus=1) Add a vcpus=1 token to the virtiofs device-options string so each device exposes a single virtio queue. This bounds the number of concurrent guest-memory apertures the device can request, avoiding the host VID's 512-aperture quota that wsldevicehost otherwise livelocks against when servicing many parallel requests. - Define a shared c_vcpusOption ("vcpus=1") constant in GuestDeviceManager.h, with a TODO to revisit once the devicehost supports multiple shares per device. - HcsVirtualMachine::AddShare and WslCoreVm::AddVirtioFsShare append the swiotlb and vcpus tokens via a small appendOption lambda, covering the fixed-drive, dynamic-add, and remount paths. Both tokens are constant for the VM's lifetime, so duplicates collapse to a single VirtioFsShare map entry. - Bump Microsoft.WSL.DeviceHost to 1.2.32-0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages.config | 2 +- src/windows/common/GuestDeviceManager.h | 5 +++++ src/windows/service/exe/HcsVirtualMachine.cpp | 15 +++++++++++---- src/windows/service/exe/WslCoreVm.cpp | 19 +++++++++++++------ 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/packages.config b/packages.config index 754f3721e4..99dda18732 100644 --- a/packages.config +++ b/packages.config @@ -19,7 +19,7 @@ - + diff --git a/src/windows/common/GuestDeviceManager.h b/src/windows/common/GuestDeviceManager.h index 41ef10df58..e3f85517df 100644 --- a/src/windows/common/GuestDeviceManager.h +++ b/src/windows/common/GuestDeviceManager.h @@ -10,6 +10,11 @@ inline const std::wstring c_defaultDeviceTag = L"default"; +// Use vcpus=1 so the device exposes a single virtio queue, bounding concurrent +// guest-memory apertures to avoid hitting the host VID's 512-aperture quota. +// TODO: revisit when the devicehost supports multiple shares per device. +inline const std::wstring c_vcpusOption = L"vcpus=1"; + // These device types and class IDs are implemented by the external wsldevicehost vdev. DEFINE_GUID(VIRTIO_FS_DEVICE_ID, 0x872270E1, 0xA899, 0x4AF6, 0xB4, 0x54, 0x71, 0x93, 0x63, 0x44, 0x35, 0xAD); // {872270E1-A899-4AF6-B454-7193634435AD} DEFINE_GUID(VIRTIO_FS_ADMIN_CLASS_ID, 0x7E6AD219, 0xD1B3, 0x42D5, 0xB8, 0xEE, 0xD9, 0x63, 0x24, 0xE6, 0x4F, 0xF6); // {7E6AD219-D1B3-42D5-B8EE-D96324E64FF6} diff --git a/src/windows/service/exe/HcsVirtualMachine.cpp b/src/windows/service/exe/HcsVirtualMachine.cpp index 74f4e0d890..b8093d1fde 100644 --- a/src/windows/service/exe/HcsVirtualMachine.cpp +++ b/src/windows/service/exe/HcsVirtualMachine.cpp @@ -585,15 +585,22 @@ try else { std::wstring options = ReadOnly ? L"ro" : L""; - if (!m_swiotlbOption.empty()) - { + auto appendOption = [&options](const std::wstring& option) { + if (option.empty()) + { + return; + } + if (!options.empty()) { options += L";"; } - options += m_swiotlbOption; - } + options += option; + }; + + appendOption(m_swiotlbOption); + appendOption(c_vcpusOption); it->second = m_guestDeviceManager->AddGuestDevice( VIRTIO_FS_DEVICE_ID, diff --git a/src/windows/service/exe/WslCoreVm.cpp b/src/windows/service/exe/WslCoreVm.cpp index 050a519e2f..995041a81f 100644 --- a/src/windows/service/exe/WslCoreVm.cpp +++ b/src/windows/service/exe/WslCoreVm.cpp @@ -2186,18 +2186,25 @@ std::pair WslCoreVm::AddVirtioFsShare(_In_ bool Admi sharePath = std::filesystem::weakly_canonical(sharePath).wstring(); - // Append the swiotlb token here so it covers fixed-drive, dynamic add, and remount paths. - // Duplicate swiotlb tokens are harmless: VirtioFsShare parses options into a map. + // Append swiotlb and vcpus here to cover the fixed-drive, dynamic add, and remount paths. + // Safe to duplicate: both tokens are constant per VM, and VirtioFsShare collapses repeats into one map entry. std::wstring effectiveOptions(Options); - if (!m_swiotlbOption.empty()) - { + auto appendOption = [&effectiveOptions](const std::wstring& option) { + if (option.empty()) + { + return; + } + if (!effectiveOptions.empty()) { effectiveOptions += L';'; } - effectiveOptions += m_swiotlbOption; - } + effectiveOptions += option; + }; + + appendOption(m_swiotlbOption); + appendOption(c_vcpusOption); // Check if a matching share already exists. bool created = false;