diff --git a/vm/devices/virtio/virtiofs/src/integration_tests.rs b/vm/devices/virtio/virtiofs/src/integration_tests.rs index 831b8df387..62bdcca7f9 100644 --- a/vm/devices/virtio/virtiofs/src/integration_tests.rs +++ b/vm/devices/virtio/virtiofs/src/integration_tests.rs @@ -71,7 +71,7 @@ impl TestHarness { let fs = VirtioFs::new(tmpdir.path(), None).unwrap(); let driver_source = VmTaskDriverSource::new(SingleDriverBackend::new(driver.clone())); - let device = VirtioFsDevice::new(&driver_source, "testfs", fs, 0, None); + let device = VirtioFsDevice::new(&driver_source, "testfs", fs, 0, None, Some(1)); let queue_event = Event::new(); let interrupt_event = Event::new(); @@ -533,3 +533,44 @@ async fn init_negotiates_direct_io_allow_mmap(driver: DefaultDriver) { "VirtioFs should request FUSE_DIRECT_IO_ALLOW_MMAP_FLAG2 when kernel advertises it" ); } + +#[async_test] +async fn num_request_queues_default_is_clamped(driver: DefaultDriver) { + let tmpdir = tempfile::tempdir().unwrap(); + let fs = VirtioFs::new(tmpdir.path(), None).unwrap(); + let driver_source = VmTaskDriverSource::new(SingleDriverBackend::new(driver)); + let device = VirtioFsDevice::new(&driver_source, "testfs", fs, 0, None, None); + + let traits = device.traits(); + // num_request_queues should be at least 1 and at most MAX (16). + // max_queues = num_request_queues + 1 (hiprio queue). + assert!(traits.max_queues >= 2, "at least 1 request queue + hiprio"); + assert!( + traits.max_queues <= 17, + "at most 16 request queues + hiprio" + ); +} + +#[async_test] +async fn num_request_queues_explicit_zero_is_clamped_to_one(driver: DefaultDriver) { + let tmpdir = tempfile::tempdir().unwrap(); + let fs = VirtioFs::new(tmpdir.path(), None).unwrap(); + let driver_source = VmTaskDriverSource::new(SingleDriverBackend::new(driver)); + let device = VirtioFsDevice::new(&driver_source, "testfs", fs, 0, None, Some(0)); + + let traits = device.traits(); + // Some(0) should be clamped to 1 → max_queues = 2 + assert_eq!(traits.max_queues, 2); +} + +#[async_test] +async fn num_request_queues_explicit_overflow_is_clamped(driver: DefaultDriver) { + let tmpdir = tempfile::tempdir().unwrap(); + let fs = VirtioFs::new(tmpdir.path(), None).unwrap(); + let driver_source = VmTaskDriverSource::new(SingleDriverBackend::new(driver)); + let device = VirtioFsDevice::new(&driver_source, "testfs", fs, 0, None, Some(100)); + + let traits = device.traits(); + // Some(100) should be clamped to MAX_REQUEST_QUEUES (16) → max_queues = 17 + assert_eq!(traits.max_queues, 17); +} diff --git a/vm/devices/virtio/virtiofs/src/resolver.rs b/vm/devices/virtio/virtiofs/src/resolver.rs index 1f0484ece9..1b0dbfc702 100644 --- a/vm/devices/virtio/virtiofs/src/resolver.rs +++ b/vm/devices/virtio/virtiofs/src/resolver.rs @@ -44,6 +44,7 @@ impl ResolveResource for VirtioFsResolver { )?, 0, None, + None, ), #[cfg(windows)] VirtioFsBackend::SectionFs { root_path } => { @@ -53,6 +54,7 @@ impl ResolveResource for VirtioFsResolver { crate::SectionFs::new(root_path)?, 8 * 1024 * 1024 * 1024, // 8GB of shared memory, None, + None, ) } #[cfg(not(windows))] diff --git a/vm/devices/virtio/virtiofs/src/virtio.rs b/vm/devices/virtio/virtiofs/src/virtio.rs index 58d060691e..25029655f9 100644 --- a/vm/devices/virtio/virtiofs/src/virtio.rs +++ b/vm/devices/virtio/virtiofs/src/virtio.rs @@ -30,6 +30,9 @@ use zerocopy::Immutable; use zerocopy::IntoBytes; use zerocopy::KnownLayout; +/// Maximum number of request queues to advertise. +const MAX_REQUEST_QUEUES: u32 = 16; + /// PCI configuration space values for virtio-fs devices. #[repr(C)] #[derive(IntoBytes, Immutable, KnownLayout)] @@ -58,19 +61,33 @@ pub struct VirtioFsDevice { impl VirtioFsDevice { /// Creates a new `VirtioFsDevice` with the specified mount tag. + /// + /// `num_request_queues` controls how many virtio request queues the device + /// advertises. The Linux guest kernel (≥6.11) distributes I/O across + /// queues on a per-CPU basis. If `None`, defaults to the number of + /// available CPUs (capped at 16). pub fn new( driver_source: &VmTaskDriverSource, tag: &str, fs: Fs, shmem_size: u64, notify_corruption: Option>, + num_request_queues: Option, ) -> Self where Fs: 'static + fuse::Fuse + Send + Sync, { + let num_request_queues = num_request_queues + .unwrap_or_else(|| { + std::thread::available_parallelism() + .map(|n| n.get() as u32) + .unwrap_or(1) + }) + .clamp(1, MAX_REQUEST_QUEUES); + let mut config = VirtioFsDeviceConfig { tag: [0; 36], - num_request_queues: 1, + num_request_queues, }; let notify_corruption = if let Some(notify) = notify_corruption { @@ -104,7 +121,7 @@ impl VirtioDevice for VirtioFsDevice { .with_ring_event_idx(true) .with_ring_indirect_desc(true) .with_ring_packed(true), - max_queues: 2, + max_queues: self.config.num_request_queues as u16 + 1, device_register_length: self.config.as_bytes().len() as u32, shared_memory: DeviceTraitsSharedMemory { id: 0,