mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
virtio-devices: Replay in-order vhost-user queues after reconnect
When reconnecting to a vhost-user backend, Cloud Hypervisor keeps the same queues and currently restarts them from avail_idx. That can skip descriptors that were made available by the guest but not completed before the old backend crashed. For queues where VIRTIO_F_IN_ORDER was negotiated, used_idx is a safe completion boundary, so reconnect can resume from used_idx and kick the queue if work remains. Do not do this when inflight tracking is active, because the backend inflight state is the more precise recovery mechanism. Also leave queues without VIRTIO_F_IN_ORDER on the existing avail_idx path, since used_idx does not identify which descriptors completed for out-of-order devices. Signed-off-by: Peter Delevoryas <pdel@meta.com>
This commit is contained in:
committed by
Rob Bradford
parent
c070d5dfa4
commit
5b68d7694d
@@ -143,6 +143,10 @@ pub enum Error {
|
|||||||
MissingIrqFd,
|
MissingIrqFd,
|
||||||
#[error("Failed getting the available index")]
|
#[error("Failed getting the available index")]
|
||||||
GetAvailableIndex(#[source] QueueError),
|
GetAvailableIndex(#[source] QueueError),
|
||||||
|
#[error("Failed getting the used index")]
|
||||||
|
GetUsedIndex(#[source] QueueError),
|
||||||
|
#[error("Failed to kick vhost-user vring")]
|
||||||
|
VhostUserKickVring(#[source] io::Error),
|
||||||
#[error("Migration is not supported by this vhost-user device")]
|
#[error("Migration is not supported by this vhost-user device")]
|
||||||
MigrationNotSupported,
|
MigrationNotSupported,
|
||||||
#[error("Failed creating memfd")]
|
#[error("Failed creating memfd")]
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ use vmm_sys_util::timerfd::TimerFd;
|
|||||||
use super::{Error, Result, VhostUserState};
|
use super::{Error, Result, VhostUserState};
|
||||||
use crate::vhost_user::Inflight;
|
use crate::vhost_user::Inflight;
|
||||||
use crate::{
|
use crate::{
|
||||||
GuestMemoryMmap, GuestRegionMmap, MmapRegion, VirtioInterrupt, VirtioInterruptType,
|
GuestMemoryMmap, GuestRegionMmap, MmapRegion, VIRTIO_F_IN_ORDER, VirtioInterrupt,
|
||||||
get_host_address_range,
|
VirtioInterruptType, get_host_address_range,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Size of a dirty page for vhost-user.
|
// Size of a dirty page for vhost-user.
|
||||||
@@ -369,6 +369,30 @@ impl VhostUserHandle {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.set_protocol_features_vhost_user(acked_features, acked_protocol_features)?;
|
self.set_protocol_features_vhost_user(acked_features, acked_protocol_features)?;
|
||||||
|
|
||||||
|
let (vring_bases, notification_needed) =
|
||||||
|
if inflight.is_none() && acked_features & (1u64 << VIRTIO_F_IN_ORDER) != 0 {
|
||||||
|
// With in-order processing, used_idx is a contiguous completion
|
||||||
|
// boundary. Replay descriptors after it when inflight tracking is
|
||||||
|
// not available to recover them more precisely.
|
||||||
|
let mut vring_bases = Vec::with_capacity(queues.len());
|
||||||
|
let mut notification_needed = Vec::with_capacity(queues.len());
|
||||||
|
for (_, queue, _) in queues {
|
||||||
|
let used_idx = queue
|
||||||
|
.used_idx(mem, Ordering::Acquire)
|
||||||
|
.map_err(Error::GetUsedIndex)?
|
||||||
|
.0;
|
||||||
|
let avail_idx = queue
|
||||||
|
.avail_idx(mem, Ordering::Acquire)
|
||||||
|
.map_err(Error::GetAvailableIndex)?
|
||||||
|
.0;
|
||||||
|
vring_bases.push(u64::from(used_idx));
|
||||||
|
notification_needed.push(used_idx != avail_idx);
|
||||||
|
}
|
||||||
|
(Some(vring_bases), notification_needed)
|
||||||
|
} else {
|
||||||
|
(None, vec![false; queues.len()])
|
||||||
|
};
|
||||||
|
|
||||||
self.setup_vhost_user(
|
self.setup_vhost_user(
|
||||||
mem,
|
mem,
|
||||||
queues,
|
queues,
|
||||||
@@ -376,8 +400,16 @@ impl VhostUserHandle {
|
|||||||
acked_features,
|
acked_features,
|
||||||
backend_req_handler,
|
backend_req_handler,
|
||||||
inflight,
|
inflight,
|
||||||
None,
|
vring_bases.as_deref(),
|
||||||
)
|
)?;
|
||||||
|
|
||||||
|
for ((_, _, queue_evt), notification_needed) in queues.iter().zip(notification_needed) {
|
||||||
|
if notification_needed {
|
||||||
|
queue_evt.write(1).map_err(Error::VhostUserKickVring)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect_vhost_user(
|
pub fn connect_vhost_user(
|
||||||
|
|||||||
Reference in New Issue
Block a user