virtio-devices: Preserve queue indices across resume

VirtioCommon stores only activated queue eventfds, so filtered queues
are renumbered when resume injects interrupts. A sparse queue set can
therefore wake the wrong MSI-X vectors and leave completed requests
stalled after restore.

Retain each queue index alongside its eventfd during activate(). Use
the saved index for resume interrupts so each notification reaches the
queue's original vector.

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:24:51 +08:00
committed by Rob Bradford
parent 64c1bb3bfc
commit e4f0744521
+12 -9
View File
@@ -281,7 +281,7 @@ pub struct VirtioCommon {
pub paused_sync: Option<Arc<Barrier>>, pub paused_sync: Option<Arc<Barrier>>,
pub workers: Option<WorkerThreads>, pub workers: Option<WorkerThreads>,
pub queue_sizes: Vec<u16>, pub queue_sizes: Vec<u16>,
pub queue_evts: Vec<EventFd>, pub queue_evts: Vec<(u16, EventFd)>,
pub device_type: u32, pub device_type: u32,
pub min_queues: u16, pub min_queues: u16,
pub access_platform: Option<Arc<dyn AccessPlatform>>, pub access_platform: Option<Arc<dyn AccessPlatform>>,
@@ -321,11 +321,14 @@ impl VirtioCommon {
self.queue_evts = queues self.queue_evts = queues
.iter() .iter()
.map(|(_, _, queue_evt)| { .map(|(queue_index, _, queue_evt)| {
queue_evt.try_clone().map_err(|e| { queue_evt
error!("failed cloning queue EventFd: {e}"); .try_clone()
ActivateError::BadActivate .map(|queue_evt| (*queue_index, queue_evt))
}) .map_err(|e| {
error!("Failed cloning queue EventFd: {e}");
ActivateError::BadActivate
})
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
@@ -493,7 +496,7 @@ impl Pausable for VirtioCommon {
// Signal each activated queue eventfd so workers process restored queues // Signal each activated queue eventfd so workers process restored queues
// that may already contain pending requests. // that may already contain pending requests.
for queue_evt in &self.queue_evts { for (_, queue_evt) in &self.queue_evts {
queue_evt.write(1).map_err(|e| { queue_evt.write(1).map_err(|e| {
MigratableError::Resume(anyhow!( MigratableError::Resume(anyhow!(
"Could not notify restored virtio worker on resume: {e}" "Could not notify restored virtio worker on resume: {e}"
@@ -502,8 +505,8 @@ impl Pausable for VirtioCommon {
} }
// Also trigger interrupts into the guest to wake up the driver to avoid a "livelock" // Also trigger interrupts into the guest to wake up the driver to avoid a "livelock"
for i in 0..self.queue_evts.len() { for (queue_index, _) in &self.queue_evts {
self.trigger_interrupt(crate::VirtioInterruptType::Queue(i as u16)) self.trigger_interrupt(crate::VirtioInterruptType::Queue(*queue_index))
.ok(); .ok();
} }