virtio-devices: Simplify interrupt handling

Previously the interrupt was created in VirtioPciDevice, moved via the
Option::take() to the VirtioPciDeviceActivator and then moved to the
VirtioDevice upon activation. On reset it would be moved back ready for
reactivation.

Since this already an Arc type remove the wrapping Option and instead
refcount it such that the VirtioPciDevice can continue to hold onto it
for later activations.

This significantly simplifies the reset() logic as there is no need to
hand back the interrupt.

A few devices used whether the interrupt was Some to make triggering an
interrupt a no-op. However the MSI-X interrupt routing already drops the
interrupt if the driver hasn't yet configured the vector so it is safe
to trigger the interrupt before device activation (e.g. balloon resize
request before driver loaded).

VirtioCommon still retains an Option<..> for the interrupt as the
interrupt is not known until activation time (after this has been
created). A helper VirtioCommon::trigger_interrupt() has been added to
handle this.

Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-04-26 10:57:49 +01:00
parent fe6bcd0376
commit e11ff541da
18 changed files with 73 additions and 110 deletions

View File

@@ -28,7 +28,7 @@ use super::{DEFAULT_VIRTIO_FEATURES, Error, Result};
use crate::seccomp_filters::Thread;
use crate::thread_helper::spawn_virtio_thread;
use crate::vhost_user::{VhostUserCommon, VhostUserState};
use crate::{GuestMemoryMmap, GuestRegionMmap, VIRTIO_F_ACCESS_PLATFORM, VirtioInterrupt};
use crate::{GuestMemoryMmap, GuestRegionMmap, VIRTIO_F_ACCESS_PLATFORM};
const DEFAULT_QUEUE_NUMBER: usize = 1;
@@ -307,8 +307,8 @@ impl VirtioDevice for Blk {
Ok(())
}
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
self.vu_common.reset(&self.id)
fn reset(&mut self) {
self.vu_common.reset(&self.id);
}
fn shutdown(&mut self) {

View File

@@ -25,7 +25,7 @@ use crate::thread_helper::spawn_virtio_thread;
use crate::vhost_user::{VhostUserCommon, VhostUserState};
use crate::{
ActivateResult, GuestMemoryMmap, GuestRegionMmap, MmapRegion, VIRTIO_F_ACCESS_PLATFORM,
VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterrupt, VirtioSharedMemoryList,
VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioSharedMemoryList,
};
const NUM_QUEUE_OFFSET: usize = 1;
@@ -285,8 +285,8 @@ impl VirtioDevice for Fs {
Ok(())
}
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
self.vu_common.reset(&self.id)
fn reset(&mut self) {
self.vu_common.reset(&self.id);
}
fn shutdown(&mut self) {

View File

@@ -26,7 +26,7 @@ use crate::thread_helper::spawn_virtio_thread;
use crate::vhost_user::{VhostUserCommon, VhostUserState};
use crate::{
ActivateResult, GuestMemoryMmap, GuestRegionMmap, MmapRegion, VIRTIO_F_ACCESS_PLATFORM,
VirtioCommon, VirtioDevice, VirtioInterrupt, VirtioSharedMemoryList,
VirtioCommon, VirtioDevice, VirtioSharedMemoryList,
};
pub type State = VhostUserState<()>;
@@ -308,8 +308,8 @@ impl VirtioDevice for GenericVhostUser {
Ok(())
}
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
self.vu_common.reset(&self.id)
fn reset(&mut self) {
self.vu_common.reset(&self.id);
}
fn shutdown(&mut self) {

View File

@@ -347,7 +347,6 @@ pub struct VhostUserCommon {
pub vu_num_queues: usize,
pub migration_started: bool,
pub server: bool,
pub interrupt_cb: Option<Arc<dyn VirtioInterrupt>>,
pub vring_bases: Option<Vec<u64>>,
pub epoll_thread: Option<thread::JoinHandle<()>>,
}
@@ -395,8 +394,6 @@ impl VhostUserCommon {
)
.map_err(ActivateError::VhostUserSetup)?;
self.interrupt_cb = Some(interrupt_cb.clone());
Ok(VhostUserEpollHandler {
vu: vu.clone(),
mem,
@@ -428,7 +425,7 @@ impl VhostUserCommon {
Ok(())
}
pub fn reset(&mut self, id: &str) -> Option<Arc<dyn VirtioInterrupt>> {
pub fn reset(&mut self, id: &str) {
// Resume the virtio thread if it was paused. Reset must always
// converge to fresh state, so backend resume / reset failures are
// logged but don't skip the rest of the teardown.
@@ -454,8 +451,8 @@ impl VhostUserCommon {
event!("virtio-device", "reset", "id", id);
// Return the interrupt
Some(self.virtio_common.interrupt_cb.take().unwrap())
// Drop the interrupt callback clone
self.virtio_common.interrupt_cb = None;
}
pub fn shutdown(&mut self) {
@@ -525,12 +522,10 @@ impl VhostUserCommon {
MigratableError::Resume(anyhow!("Error resuming vhost-user backend: {e:?}"))
})?;
}
if let Some(interrupt_cb) = &self.interrupt_cb {
for i in 0..self.vu_num_queues {
interrupt_cb
.trigger(crate::VirtioInterruptType::Queue(i as u16))
.ok();
}
for i in 0..self.vu_num_queues {
self.virtio_common
.trigger_interrupt(crate::VirtioInterruptType::Queue(i as u16))
.ok();
}
Ok(())
}

View File

@@ -29,7 +29,7 @@ use crate::vhost_user::vu_common_ctrl::{VhostUserConfig, VhostUserHandle};
use crate::vhost_user::{DEFAULT_VIRTIO_FEATURES, Error, Result, VhostUserCommon, VhostUserState};
use crate::{
ActivateResult, GuestMemoryMmap, GuestRegionMmap, NetCtrlEpollHandler,
VIRTIO_F_ACCESS_PLATFORM, VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterrupt,
VIRTIO_F_ACCESS_PLATFORM, VirtioCommon, VirtioDevice, VirtioDeviceType,
};
const DEFAULT_QUEUE_NUMBER: usize = 2;
@@ -364,8 +364,8 @@ impl VirtioDevice for Net {
Ok(())
}
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
self.vu_common.reset(&self.id)
fn reset(&mut self) {
self.vu_common.reset(&self.id);
}
fn shutdown(&mut self) {