Force virtiofs devices to a single virtio queue (vcpus=1) (#40758)

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.34 Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>

Asher Kariv committed Jun 18, 2026 at 17:28 UTC 532422e00279c648c033397ff43c6455d5ffcf40
4 files changed +30 -11
packages.config
+1 -1
@@ -19,7 +19,7 @@
19 <package id="Microsoft.WSL.bsdtar" version="0.0.2-2" />
20 <package id="Microsoft.WSL.Dependencies.amd64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
21 <package id="Microsoft.WSL.Dependencies.arm64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
22 - <package id="Microsoft.WSL.DeviceHost" version="1.2.29-0" />
22 + <package id="Microsoft.WSL.DeviceHost" version="1.2.34-0" />
23 <package id="Microsoft.WSL.Kernel" version="6.18.35.1-1" targetFramework="native" />
24 <package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
25 <package id="Microsoft.WSL.TestData" version="0.4.0" />
src/windows/common/GuestDeviceManager.h
+5
@@ -10,6 +10,11 @@
10
11 inline const std::wstring c_defaultDeviceTag = L"default";
12
13 +// Use vcpus=1 so the device exposes a single virtio queue, bounding concurrent
14 +// guest-memory apertures to avoid hitting the host VID's 512-aperture quota.
15 +// TODO: revisit when the devicehost supports multiple shares per device.
16 +inline const std::wstring c_vcpusOption = L"vcpus=1";
17 +
18 // These device types and class IDs are implemented by the external wsldevicehost vdev.
19 DEFINE_GUID(VIRTIO_FS_DEVICE_ID, 0x872270E1, 0xA899, 0x4AF6, 0xB4, 0x54, 0x71, 0x93, 0x63, 0x44, 0x35, 0xAD); // {872270E1-A899-4AF6-B454-7193634435AD}
20 DEFINE_GUID(VIRTIO_FS_ADMIN_CLASS_ID, 0x7E6AD219, 0xD1B3, 0x42D5, 0xB8, 0xEE, 0xD9, 0x63, 0x24, 0xE6, 0x4F, 0xF6); // {7E6AD219-D1B3-42D5-B8EE-D96324E64FF6}
src/windows/service/exe/HcsVirtualMachine.cpp
+11 -4
@@ -587,15 +587,22 @@ try
587 else
588 {
589 std::wstring options = ReadOnly ? L"ro" : L"";
590 - if (!m_swiotlbOption.empty())
591 - {
590 + auto appendOption = [&options](const std::wstring& option) {
591 + if (option.empty())
592 + {
593 + return;
594 + }
595 +
596 if (!options.empty())
597 {
598 options += L";";
599 }
600
597 - options += m_swiotlbOption;
598 - }
601 + options += option;
602 + };
603 +
604 + appendOption(m_swiotlbOption);
605 + appendOption(c_vcpusOption);
606
607 it->second = m_guestDeviceManager->AddGuestDevice(
608 VIRTIO_FS_DEVICE_ID,
src/windows/service/exe/WslCoreVm.cpp
+13 -6
@@ -2195,18 +2195,25 @@ std::pair<std::wstring, std::wstring> WslCoreVm::AddVirtioFsShare(_In_ bool Admi
2195
2196 sharePath = std::filesystem::weakly_canonical(sharePath).wstring();
2197
2198 - // Append the swiotlb token here so it covers fixed-drive, dynamic add, and remount paths.
2199 - // Duplicate swiotlb tokens are harmless: VirtioFsShare parses options into a map.
2198 + // Append swiotlb and vcpus here to cover the fixed-drive, dynamic add, and remount paths.
2199 + // Safe to duplicate: both tokens are constant per VM, and VirtioFsShare collapses repeats into one map entry.
2200 std::wstring effectiveOptions(Options);
2201 - if (!m_swiotlbOption.empty())
2202 - {
2201 + auto appendOption = [&effectiveOptions](const std::wstring& option) {
2202 + if (option.empty())
2203 + {
2204 + return;
2205 + }
2206 +
2207 if (!effectiveOptions.empty())
2208 {
2209 effectiveOptions += L';';
2210 }
2211
2208 - effectiveOptions += m_swiotlbOption;
2209 - }
2212 + effectiveOptions += option;
2213 + };
2214 +
2215 + appendOption(m_swiotlbOption);
2216 + appendOption(c_vcpusOption);
2217
2218 // Check if a matching share already exists.
2219 bool created = false;