virtio-devices: Test sparse queue indices on resume

Filtered activation queues can hide regressions where resume treats
their positions as transport queue indices. Such regressions route
interrupts to unrelated MSI-X vectors while eventfd notifications still
reach the correct workers.

Add a VirtioCommon regression test with queue indices one and three.
Verify resume signals both queue eventfds and records interrupts for the
original sparse indices.

Assisted-by: OpenAI:GPT-5.6-Sol
Signed-off-by: Yi Wang <foxywang@tencent.com>
This commit is contained in:
Yi Wang
2026-07-22 22:26:41 +08:00
committed by Rob Bradford
parent e4f0744521
commit e0f2d99d6f

View File

@@ -516,12 +516,37 @@ impl Pausable for VirtioCommon {
#[cfg(test)] #[cfg(test)]
mod unit_tests { mod unit_tests {
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use virtio_queue::QueueT;
use vmm_sys_util::eventfd::EFD_NONBLOCK; use vmm_sys_util::eventfd::EFD_NONBLOCK;
use super::*; use super::*;
#[derive(Default)]
struct RecordingInterrupt {
queue_indices: Mutex<Vec<u16>>,
}
impl VirtioInterrupt for RecordingInterrupt {
fn trigger(&self, int_type: VirtioInterruptType) -> io::Result<()> {
if let VirtioInterruptType::Queue(queue_index) = int_type {
self.queue_indices.lock().unwrap().push(queue_index);
}
Ok(())
}
fn set_notifier(
&self,
_: u32,
_: Option<EventFd>,
_: &dyn hypervisor::Vm,
) -> io::Result<()> {
Ok(())
}
}
struct NoopInterrupt; struct NoopInterrupt;
impl VirtioInterrupt for NoopInterrupt { impl VirtioInterrupt for NoopInterrupt {
fn trigger(&self, _: VirtioInterruptType) -> io::Result<()> { fn trigger(&self, _: VirtioInterruptType) -> io::Result<()> {
@@ -616,6 +641,31 @@ mod unit_tests {
assert_eq!(started.load(Ordering::SeqCst), 1); assert_eq!(started.load(Ordering::SeqCst), 1);
} }
#[test]
fn resume_preserves_sparse_queue_indices() {
let interrupt = Arc::new(RecordingInterrupt::default());
let queues = vec![
(
1,
Queue::new(256).unwrap(),
EventFd::new(EFD_NONBLOCK).unwrap(),
),
(
3,
Queue::new(256).unwrap(),
EventFd::new(EFD_NONBLOCK).unwrap(),
),
];
let mut common = VirtioCommon::default();
common.activate(&queues, interrupt.clone()).unwrap();
common.resume().unwrap();
assert_eq!(queues[0].2.read().unwrap(), 1);
assert_eq!(queues[1].2.read().unwrap(), 1);
assert_eq!(*interrupt.queue_indices.lock().unwrap(), vec![1, 3]);
}
#[test] #[test]
fn reset_clears_paused_without_workers() { fn reset_clears_paused_without_workers() {
// reset() before any worker was spawned must still clear paused, or // reset() before any worker was spawned must still clear paused, or