mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
virtio-devices: signal activated queue eventfds on resume
A restored virtqueue can already contain pending descriptors when the VM resumes. Before this change, the worker thread was unparked and then waited for a fresh queue eventfd signal. That is normally fine, but not when the queue was already non-empty at snapshot time. The virtqueue state lives in guest memory and is restored, but the original host-side queue eventfd signal is not persistent snapshot state. If the guest already notified the queue before the snapshot, it may not notify it again after resume. That can leave the worker idle while the guest is still waiting for the pending request to complete. In one observed case, this stalled a virtio-blk flush during early boot after snapshot/restore. We mitigate this in the shared `VirtioCommon` resume path. `VirtioCommon` retains cloned queue eventfds for activated virtqueues and signals each of them once on resume after unparking the worker threads. Keep virtio-net on its existing special-case path: it resumes worker threads without signaling queue eventfds so the `driver_awake` workaround remains intact until the guest performs a real notify. On-behalf-of: SAP leander.kohler@sap.com Signed-off-by: Leander Kohler <leander.kohler@cyberus-technology.de>
This commit is contained in:
committed by
Rob Bradford
parent
df58e814eb
commit
3e3008f365
@@ -13,6 +13,7 @@ use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::thread;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use libc::EFD_NONBLOCK;
|
||||
use log::{error, info, warn};
|
||||
use virtio_queue::Queue;
|
||||
@@ -215,6 +216,7 @@ pub struct VirtioCommon {
|
||||
pub paused_sync: Option<Arc<Barrier>>,
|
||||
pub epoll_threads: Option<Vec<thread::JoinHandle<()>>>,
|
||||
pub queue_sizes: Vec<u16>,
|
||||
pub queue_evts: Vec<EventFd>,
|
||||
pub device_type: u32,
|
||||
pub min_queues: u16,
|
||||
pub access_platform: Option<Arc<dyn AccessPlatform>>,
|
||||
@@ -252,6 +254,21 @@ impl VirtioCommon {
|
||||
return Err(ActivateError::BadActivate);
|
||||
}
|
||||
|
||||
// Do not retain virtio-net queue eventfds here. Signaling them on
|
||||
// resume would break its `driver_awake` workaround.
|
||||
self.queue_evts = match VirtioDeviceType::from(self.device_type) {
|
||||
VirtioDeviceType::Net => Vec::new(),
|
||||
_ => queues
|
||||
.iter()
|
||||
.map(|(_, _, queue_evt)| {
|
||||
queue_evt.try_clone().map_err(|e| {
|
||||
error!("failed cloning queue EventFd: {e}");
|
||||
ActivateError::BadActivate
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
};
|
||||
|
||||
let kill_evt = EventFd::new(EFD_NONBLOCK).map_err(|e| {
|
||||
error!("failed creating kill EventFd: {e}");
|
||||
ActivateError::BadActivate
|
||||
@@ -272,6 +289,8 @@ impl VirtioCommon {
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
||||
self.queue_evts.clear();
|
||||
|
||||
// We first must resume the virtio thread if it was paused.
|
||||
if self.pause_evt.take().is_some() {
|
||||
self.resume().ok()?;
|
||||
@@ -355,6 +374,16 @@ impl Pausable for VirtioCommon {
|
||||
}
|
||||
}
|
||||
|
||||
// Signal each activated queue eventfd so workers process restored queues
|
||||
// that may already contain pending requests.
|
||||
for queue_evt in &self.queue_evts {
|
||||
queue_evt.write(1).map_err(|e| {
|
||||
MigratableError::Resume(anyhow!(
|
||||
"Could not notify restored virtio worker on resume: {e}"
|
||||
))
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user