Skip to content

Commit 13879c6

Browse files
committed
refactor(pci): simplify MSI-X interrupts implementation
Our implementation of MSI-X was taken from the equivalent Cloud Hypervisor one. Cloud Hypervisor supports non-KVM hypervisors, hence they introduced a few abstractions to cater for that. More specifically, they are using a trait (`InterruptSourceGroup`) to describe the underlying hypervisor-specific mechanism used to drive MSI-X interrupts from the device to the guest. Moreover, they use a couple of types to distinguish between MSI, MSI-X and legacy IRQ interrupts and how these can be configured. This is especially useful for MSI/MSI-X interrupts where the interrupt configuration is provided by the guest over PCI bus MMIO accesses. Finally, in order to allow for transparent error handling, `Result` types returned by related trait methods was returning `std::io::Error` types as the error type, which is not very intuitive. We don't need all of that. We clearly distinguish between legacy interrupts and MSI-X (we don't use at all MSI), so we can simply plug in all the definitions directly where we need them without any dynamic dispatch indirection. Moreover, various MSI-X components were stored all over the place creating a lot of unnecessary duplication. This commit refactors our implementation to get rid of the unnecessary bit of dynamic dispatch. We still keep the `VirtioInterrupt` trait which we use to abstract the actual interrupt type (IRQ or MSI-X) from VirtIO devices. Moreover, it removes the duplication of MSI-X related types across various types. We only duplicate the MSI-X configuration type (which is behind an Arc) in a few places to avoid needing to unnecessarily take a lock(). Moreover, we introduce a single interrupt related error type and get rid of the `std::io::Error` usage. Finally, it introduces a couple of metrics for interrupts that are useful information by themselves, but also help us maintain some unit testing assertions for which we were relying on a mock type that implemented `InterruptSourceGroup`. Signed-off-by: Babis Chalios <[email protected]>
1 parent b49d4c9 commit 13879c6

File tree

13 files changed

+436
-527
lines changed

13 files changed

+436
-527
lines changed

src/vmm/src/device_manager/pci_mngr.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ use crate::pci::bus::PciRootError;
3434
use crate::resources::VmResources;
3535
use crate::snapshot::Persist;
3636
use crate::vmm_config::mmds::MmdsConfigError;
37+
use crate::vstate::interrupts::InterruptError;
3738
use crate::vstate::memory::GuestMemoryMmap;
38-
use crate::vstate::vm::{InterruptError, MsiVectorGroup};
3939
use crate::{EventManager, Vm};
4040

4141
#[derive(Debug, Default)]
@@ -121,11 +121,16 @@ impl PciDevices {
121121
let msix_num =
122122
u16::try_from(device.lock().expect("Poisoned lock").queues().len() + 1).unwrap();
123123

124-
let msix_vectors = Arc::new(Vm::create_msix_group(vm.clone(), msix_num)?);
124+
let msix_vectors = Vm::create_msix_group(vm.clone(), msix_num)?;
125125

126126
// Create the transport
127-
let mut virtio_device =
128-
VirtioPciDevice::new(id.clone(), mem, device, msix_vectors, pci_device_bdf.into())?;
127+
let mut virtio_device = VirtioPciDevice::new(
128+
id.clone(),
129+
mem,
130+
device,
131+
Arc::new(msix_vectors),
132+
pci_device_bdf.into(),
133+
)?;
129134

130135
// Allocate bars
131136
let mut resource_allocator_lock = vm.resource_allocator();
@@ -162,17 +167,12 @@ impl PciDevices {
162167
) -> Result<(), PciManagerError> {
163168
// We should only be reaching this point if PCI is enabled
164169
let pci_segment = self.pci_segment.as_ref().unwrap();
165-
let msi_vector_group = Arc::new(MsiVectorGroup::restore(
166-
vm.clone(),
167-
&transport_state.msi_vector_group,
168-
)?);
169170
let device_type: u32 = device.lock().expect("Poisoned lock").device_type();
170171

171172
let virtio_device = Arc::new(Mutex::new(VirtioPciDevice::new_from_state(
172173
device_id.to_string(),
173-
vm.guest_memory().clone(),
174+
vm,
174175
device.clone(),
175-
msi_vector_group,
176176
transport_state.clone(),
177177
)?));
178178

src/vmm/src/devices/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::devices::virtio::net::metrics::NetDeviceMetrics;
2323
use crate::devices::virtio::queue::{InvalidAvailIdx, QueueError};
2424
use crate::devices::virtio::vsock::VsockError;
2525
use crate::logger::IncMetric;
26+
use crate::vstate::interrupts::InterruptError;
2627

2728
// Function used for reporting error in terms of logging
2829
// but also in terms of metrics of net event fails.
@@ -41,7 +42,7 @@ pub enum DeviceError {
4142
/// Failed to read from the TAP device.
4243
FailedReadTap,
4344
/// Failed to signal irq: {0}
44-
FailedSignalingIrq(io::Error),
45+
FailedSignalingIrq(#[from] InterruptError),
4546
/// IO error: {0}
4647
IoError(io::Error),
4748
/// Device received malformed payload.

src/vmm/src/devices/virtio/balloon/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use super::queue::{InvalidAvailIdx, QueueError};
1717
use crate::devices::virtio::balloon::metrics::METRICS;
1818
use crate::devices::virtio::queue::FIRECRACKER_MAX_QUEUE_SIZE;
1919
use crate::logger::IncMetric;
20+
use crate::vstate::interrupts::InterruptError;
2021

2122
/// Device ID used in MMIO device identification.
2223
/// Because Balloon is unique per-vm, this ID can be hardcoded.
@@ -72,7 +73,7 @@ pub enum BalloonError {
7273
/// EventFd error: {0}
7374
EventFd(std::io::Error),
7475
/// Received error while sending an interrupt: {0}
75-
InterruptError(std::io::Error),
76+
InterruptError(InterruptError),
7677
/// Guest gave us a malformed descriptor.
7778
MalformedDescriptor,
7879
/// Guest gave us a malformed payload.

src/vmm/src/devices/virtio/block/vhost_user/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod persist;
77

88
use self::device::VhostUserBlock;
99
use crate::devices::virtio::vhost_user::VhostUserError;
10+
use crate::vstate::interrupts::InterruptError;
1011

1112
/// Number of queues for the vhost-user block device.
1213
pub const NUM_QUEUES: u64 = 1;
@@ -28,5 +29,5 @@ pub enum VhostUserBlockError {
2829
/// Error opening eventfd: {0}
2930
EventFd(std::io::Error),
3031
/// Error creating irqfd: {0}
31-
Interrupt(std::io::Error),
32+
Interrupt(InterruptError),
3233
}

src/vmm/src/devices/virtio/transport/mmio.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ use super::{VirtioInterrupt, VirtioInterruptType};
1515
use crate::devices::virtio::device::VirtioDevice;
1616
use crate::devices::virtio::device_status;
1717
use crate::devices::virtio::queue::Queue;
18-
use crate::logger::{error, warn};
18+
use crate::logger::{IncMetric, METRICS, error, warn};
1919
use crate::utils::byte_order;
20+
use crate::vstate::interrupts::InterruptError;
2021
use crate::vstate::memory::{GuestAddress, GuestMemoryMmap};
2122

2223
// TODO crosvm uses 0 here, but IIRC virtio specified some other vendor id that should be used
@@ -399,17 +400,19 @@ impl Default for IrqTrigger {
399400
}
400401

401402
impl VirtioInterrupt for IrqTrigger {
402-
fn trigger(&self, interrupt_type: VirtioInterruptType) -> Result<(), std::io::Error> {
403+
fn trigger(&self, interrupt_type: VirtioInterruptType) -> Result<(), InterruptError> {
404+
METRICS.interrupts.triggers.inc();
403405
match interrupt_type {
404406
VirtioInterruptType::Config => self.trigger_irq(IrqType::Config),
405407
VirtioInterruptType::Queue(_) => self.trigger_irq(IrqType::Vring),
406408
}
407409
}
408410

409-
fn trigger_queues(&self, queues: &[u16]) -> Result<(), std::io::Error> {
411+
fn trigger_queues(&self, queues: &[u16]) -> Result<(), InterruptError> {
410412
if queues.is_empty() {
411413
Ok(())
412414
} else {
415+
METRICS.interrupts.triggers.inc();
413416
self.trigger_irq(IrqType::Vring)
414417
}
415418
}
@@ -449,7 +452,7 @@ impl IrqTrigger {
449452
}
450453
}
451454

452-
fn trigger_irq(&self, irq_type: IrqType) -> Result<(), std::io::Error> {
455+
fn trigger_irq(&self, irq_type: IrqType) -> Result<(), InterruptError> {
453456
let irq = match irq_type {
454457
IrqType::Config => VIRTIO_MMIO_INT_CONFIG,
455458
IrqType::Vring => VIRTIO_MMIO_INT_VRING,

src/vmm/src/devices/virtio/transport/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use std::sync::atomic::AtomicU32;
66

77
use vmm_sys_util::eventfd::EventFd;
88

9+
use crate::vstate::interrupts::InterruptError;
10+
911
/// MMIO transport for VirtIO devices
1012
pub mod mmio;
1113
/// PCI transport for VirtIO devices
@@ -23,15 +25,15 @@ pub enum VirtioInterruptType {
2325
/// API of interrupt types used by VirtIO devices
2426
pub trait VirtioInterrupt: std::fmt::Debug + Send + Sync {
2527
/// Trigger a VirtIO interrupt.
26-
fn trigger(&self, interrupt_type: VirtioInterruptType) -> Result<(), std::io::Error>;
28+
fn trigger(&self, interrupt_type: VirtioInterruptType) -> Result<(), InterruptError>;
2729

2830
/// Trigger multiple Virtio interrupts for selected queues.
2931
/// The caller needs to ensure that [`queues`] does not include duplicate entries to
3032
/// avoid sending multiple interrupts for the same queue.
3133
/// This is to allow sending a single interrupt for implementations that don't
3234
/// distinguish different queues, like IrqTrigger, instead of sending multiple same
3335
/// interrupts.
34-
fn trigger_queues(&self, queues: &[u16]) -> Result<(), std::io::Error> {
36+
fn trigger_queues(&self, queues: &[u16]) -> Result<(), InterruptError> {
3537
queues
3638
.iter()
3739
.try_for_each(|&qidx| self.trigger(VirtioInterruptType::Queue(qidx)))

0 commit comments

Comments
 (0)