pci: Support injecting interrupts from externally-provided irqfds

The virtio vhost-user device backend prefers to use externally-provided
eventfds as irqfds.  This allows the frontend VM to notify the backend
VM directly, without the need for a userspace proxy process.  Since the
frontend can provide irqfds at any time, the backend needs to register
and unregister irqfds dynamically.

This is tricky because the functions that access the irqfd table all
take `&self`, not `&mut self`.  The obvious solution to this problem is
to wrap the table in a mutex.  Most of these functions are not called on
hot paths, but `.notifier()` is called whenever Cloud Hypervisor needs
to inject an interrupt into a guest.  Most devices don't need to
register irqfds at runtime, and for them, slowing down interrupt
injection would be wasteful.

Instead, require devices to opt-in to irqfd registration.  The irqfd
table now comes in two forms: one that contains a mutex and one that
does not.  The one containing a mutex can be mutated freely, while
attempting to mutate the one that does not will panic.

Right now, no code registeres irqfds at runtime, but this will change in
subsequent commits.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
This commit is contained in:
Demi Marie Obenour
2026-02-11 20:08:45 -05:00
committed by Sebastien Boeuf
parent 9f62c33d00
commit d609410b8b
8 changed files with 194 additions and 33 deletions

View File

@@ -32,7 +32,10 @@ pub use self::device::{
BarReprogrammingParams, DeviceRelocation, Error as PciDeviceError, PciDevice,
};
pub use self::msi::{MsiCap, MsiConfig, msi_num_enabled_vectors};
pub use self::msix::{MSIX_CONFIG_ID, MSIX_TABLE_ENTRY_SIZE, MsixCap, MsixConfig, MsixTableEntry};
pub use self::msix::{
MSIX_CONFIG_ID, MSIX_TABLE_ENTRY_SIZE, MaybeMutInterruptSourceGroup, MsixCap, MsixConfig,
MsixTableEntry,
};
pub use self::vfio::{MmioRegion, VfioDmaMapping, VfioPciDevice, VfioPciError};
pub use self::vfio_user::{VfioUserDmaMapping, VfioUserPciDevice, VfioUserPciDeviceError};

View File

@@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
use std::sync::Arc;
use std::sync::{Arc, Mutex};
use std::{io, result};
use byteorder::{ByteOrder, LittleEndian};
@@ -15,6 +15,7 @@ use vm_device::interrupt::{
};
use vm_memory::ByteValued;
use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable};
use vmm_sys_util::eventfd::EventFd;
use crate::{PciCapability, PciCapabilityId};
@@ -72,11 +73,66 @@ pub struct MsixConfigState {
enabled: bool,
}
#[derive(Clone)]
pub enum MaybeMutInterruptSourceGroup {
Immutable(Arc<dyn InterruptSourceGroup>),
Mutable(Arc<Mutex<dyn InterruptSourceGroup>>),
}
macro_rules! impl_method {
($(
fn $i: ident(&self $(,$index:ident : $InterruptIndex:ty)*$(,)?) -> $r: ty;
)*) => {
$(
fn $i(&self $(,$index: $InterruptIndex)*) -> $r {
match self {
Self::Immutable(source) => source.$i($($index),*),
Self::Mutable(source) => source.lock().unwrap().$i($($index),*),
}
}
)*
};
}
impl InterruptSourceGroup for MaybeMutInterruptSourceGroup {
impl_method! {
fn trigger(&self, index: InterruptIndex) -> vm_device::interrupt::Result<()>;
fn notifier(&self, index: InterruptIndex) -> Option<vmm_sys_util::eventfd::EventFd>;
fn update(
&self,
index: InterruptIndex,
config: InterruptSourceConfig,
masked: bool,
set_gsi: bool,
) -> vm_device::interrupt::Result<()>;
fn set_gsi(&self) -> vm_device::interrupt::Result<()>;
}
}
impl MaybeMutInterruptSourceGroup {
pub fn set_notifier(
&self,
index: InterruptIndex,
eventfd: Option<EventFd>,
vm: &dyn hypervisor::Vm,
) -> std::io::Result<()> {
match self {
Self::Immutable(_) => panic!(
"Attempted to set a notifier of an immutable source. You must mark your device as needing a mutable source by having sets_irqfd() return true."
),
Self::Mutable(source) => source.lock().unwrap().set_notifier(index, eventfd, vm),
}
}
}
pub struct MsixConfig {
pub table_entries: Vec<MsixTableEntry>,
pub pba_entries: Vec<u64>,
pub devid: u32,
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
interrupt_source_group: MaybeMutInterruptSourceGroup,
masked: bool,
enabled: bool,
}
@@ -84,7 +140,7 @@ pub struct MsixConfig {
impl MsixConfig {
pub fn new(
msix_vectors: u16,
interrupt_source_group: Arc<dyn InterruptSourceGroup>,
interrupt_source_group: MaybeMutInterruptSourceGroup,
devid: u32,
state: Option<MsixConfigState>,
) -> result::Result<Self, Error> {

View File

@@ -37,7 +37,7 @@ use vmm_sys_util::eventfd::EventFd;
use crate::mmap::MmapRegion;
use crate::msi::{MSI_CONFIG_ID, MsiConfigState};
use crate::msix::MsixConfigState;
use crate::msix::{MaybeMutInterruptSourceGroup, MsixConfigState};
use crate::{
BarReprogrammingParams, MSIX_CONFIG_ID, MSIX_TABLE_ENTRY_SIZE, MsiCap, MsiConfig, MsixCap,
MsixConfig, PCI_CONFIGURATION_ID, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType,
@@ -863,7 +863,7 @@ impl VfioCommon {
let msix_config = MsixConfig::new(
msix_cap.table_size(),
interrupt_source_group.clone(),
MaybeMutInterruptSourceGroup::Immutable(interrupt_source_group.clone()),
bdf.into(),
state,
)