mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-01-03 03:15:20 +00:00
pci: vfio: Move interrupt handling to VfioCommon
The interrupt handling code can be reused with the vfio-user implementation. Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
parent
521a11a110
commit
ecc8382ff0
352
pci/src/vfio.rs
352
pci/src/vfio.rs
@ -277,6 +277,22 @@ pub(crate) trait Vfio {
|
||||
self.region_write(VFIO_PCI_CONFIG_REGION_INDEX, offset.into(), data)
|
||||
}
|
||||
|
||||
fn enable_msi(&self, fds: Vec<&EventFd>) -> std::result::Result<(), VfioError> {
|
||||
self.enable_irq(VFIO_PCI_MSI_IRQ_INDEX, fds)
|
||||
}
|
||||
|
||||
fn disable_msi(&self) -> std::result::Result<(), VfioError> {
|
||||
self.disable_irq(VFIO_PCI_MSI_IRQ_INDEX)
|
||||
}
|
||||
|
||||
fn enable_msix(&self, fds: Vec<&EventFd>) -> std::result::Result<(), VfioError> {
|
||||
self.enable_irq(VFIO_PCI_MSIX_IRQ_INDEX, fds)
|
||||
}
|
||||
|
||||
fn disable_msix(&self) -> std::result::Result<(), VfioError> {
|
||||
self.disable_irq(VFIO_PCI_MSIX_IRQ_INDEX)
|
||||
}
|
||||
|
||||
fn region_read(&self, _index: u32, _offset: u64, _data: &mut [u8]) {
|
||||
unimplemented!()
|
||||
}
|
||||
@ -288,6 +304,18 @@ pub(crate) trait Vfio {
|
||||
fn get_irq_info(&self, _irq_index: u32) -> Option<&VfioIrq> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn enable_irq(
|
||||
&self,
|
||||
_irq_index: u32,
|
||||
_event_fds: Vec<&EventFd>,
|
||||
) -> std::result::Result<(), VfioError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn disable_irq(&self, _irq_index: u32) -> std::result::Result<(), VfioError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
struct VfioDeviceWrapper {
|
||||
@ -312,6 +340,18 @@ impl Vfio for VfioDeviceWrapper {
|
||||
fn get_irq_info(&self, irq_index: u32) -> Option<&VfioIrq> {
|
||||
self.device.get_irq_info(irq_index)
|
||||
}
|
||||
|
||||
fn enable_irq(
|
||||
&self,
|
||||
irq_index: u32,
|
||||
event_fds: Vec<&EventFd>,
|
||||
) -> std::result::Result<(), VfioError> {
|
||||
self.device.enable_irq(irq_index, event_fds)
|
||||
}
|
||||
|
||||
fn disable_irq(&self, irq_index: u32) -> std::result::Result<(), VfioError> {
|
||||
self.device.disable_irq(irq_index)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct VfioCommon {
|
||||
@ -603,6 +643,157 @@ impl VfioCommon {
|
||||
cap_next = vfio_wrapper.read_config_byte((cap_next + 1).into());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enable_intx(&mut self, wrapper: &dyn Vfio) -> Result<()> {
|
||||
if let Some(intx) = &mut self.interrupt.intx {
|
||||
if !intx.enabled {
|
||||
if let Some(eventfd) = intx.interrupt_source_group.notifier(0) {
|
||||
wrapper
|
||||
.enable_irq(VFIO_PCI_INTX_IRQ_INDEX, vec![&eventfd])
|
||||
.map_err(VfioPciError::EnableIntx)?;
|
||||
|
||||
intx.enabled = true;
|
||||
} else {
|
||||
return Err(VfioPciError::MissingNotifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn disable_intx(&mut self, wrapper: &dyn Vfio) {
|
||||
if let Some(intx) = &mut self.interrupt.intx {
|
||||
if intx.enabled {
|
||||
if let Err(e) = wrapper.disable_irq(VFIO_PCI_INTX_IRQ_INDEX) {
|
||||
error!("Could not disable INTx: {}", e);
|
||||
} else {
|
||||
intx.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enable_msi(&self, wrapper: &dyn Vfio) -> Result<()> {
|
||||
if let Some(msi) = &self.interrupt.msi {
|
||||
let mut irq_fds: Vec<EventFd> = Vec::new();
|
||||
for i in 0..msi.cfg.num_enabled_vectors() {
|
||||
if let Some(eventfd) = msi.interrupt_source_group.notifier(i as InterruptIndex) {
|
||||
irq_fds.push(eventfd);
|
||||
} else {
|
||||
return Err(VfioPciError::MissingNotifier);
|
||||
}
|
||||
}
|
||||
|
||||
wrapper
|
||||
.enable_msi(irq_fds.iter().collect())
|
||||
.map_err(VfioPciError::EnableMsi)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn disable_msi(&self, wrapper: &dyn Vfio) {
|
||||
if let Err(e) = wrapper.disable_msi() {
|
||||
error!("Could not disable MSI: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enable_msix(&self, wrapper: &dyn Vfio) -> Result<()> {
|
||||
if let Some(msix) = &self.interrupt.msix {
|
||||
let mut irq_fds: Vec<EventFd> = Vec::new();
|
||||
for i in 0..msix.bar.table_entries.len() {
|
||||
if let Some(eventfd) = msix.interrupt_source_group.notifier(i as InterruptIndex) {
|
||||
irq_fds.push(eventfd);
|
||||
} else {
|
||||
return Err(VfioPciError::MissingNotifier);
|
||||
}
|
||||
}
|
||||
|
||||
wrapper
|
||||
.enable_msix(irq_fds.iter().collect())
|
||||
.map_err(VfioPciError::EnableMsix)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn disable_msix(&self, wrapper: &dyn Vfio) {
|
||||
if let Err(e) = wrapper.disable_msix() {
|
||||
error!("Could not disable MSI-X: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn initialize_legacy_interrupt(
|
||||
&mut self,
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
wrapper: &dyn Vfio,
|
||||
) -> Result<()> {
|
||||
if let Some(irq_info) = wrapper.get_irq_info(VFIO_PCI_INTX_IRQ_INDEX) {
|
||||
if irq_info.count == 0 {
|
||||
// A count of 0 means the INTx IRQ is not supported, therefore
|
||||
// it shouldn't be initialized.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(interrupt_source_group) = legacy_interrupt_group {
|
||||
self.interrupt.intx = Some(VfioIntx {
|
||||
interrupt_source_group,
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
self.enable_intx(wrapper)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn update_msi_capabilities(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
wrapper: &dyn Vfio,
|
||||
) -> Result<()> {
|
||||
match self.interrupt.update_msi(offset, data) {
|
||||
Some(InterruptUpdateAction::EnableMsi) => {
|
||||
// Disable INTx before we can enable MSI
|
||||
self.disable_intx(wrapper);
|
||||
self.enable_msi(wrapper)?;
|
||||
}
|
||||
Some(InterruptUpdateAction::DisableMsi) => {
|
||||
// Fallback onto INTx when disabling MSI
|
||||
self.disable_msi(wrapper);
|
||||
self.enable_intx(wrapper)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn update_msix_capabilities(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
data: &[u8],
|
||||
wrapper: &dyn Vfio,
|
||||
) -> Result<()> {
|
||||
match self.interrupt.update_msix(offset, data) {
|
||||
Some(InterruptUpdateAction::EnableMsix) => {
|
||||
// Disable INTx before we can enable MSI-X
|
||||
self.disable_intx(wrapper);
|
||||
self.enable_msix(wrapper)?;
|
||||
}
|
||||
Some(InterruptUpdateAction::DisableMsix) => {
|
||||
// Fallback onto INTx when disabling MSI-X
|
||||
self.disable_msix(wrapper);
|
||||
self.enable_intx(wrapper)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// VfioPciDevice represents a VFIO PCI device.
|
||||
@ -659,8 +850,9 @@ impl VfioPciDevice {
|
||||
};
|
||||
|
||||
common.parse_capabilities(msi_interrupt_manager, &vfio_wrapper);
|
||||
common.initialize_legacy_interrupt(legacy_interrupt_group, &vfio_wrapper)?;
|
||||
|
||||
let mut vfio_pci_device = VfioPciDevice {
|
||||
let vfio_pci_device = VfioPciDevice {
|
||||
vm: vm.clone(),
|
||||
device,
|
||||
container,
|
||||
@ -669,8 +861,6 @@ impl VfioPciDevice {
|
||||
iommu_attached,
|
||||
};
|
||||
|
||||
vfio_pci_device.initialize_legacy_interrupt(legacy_interrupt_group)?;
|
||||
|
||||
Ok(vfio_pci_device)
|
||||
}
|
||||
|
||||
@ -678,146 +868,6 @@ impl VfioPciDevice {
|
||||
self.iommu_attached
|
||||
}
|
||||
|
||||
fn enable_intx(&mut self) -> Result<()> {
|
||||
if let Some(intx) = &mut self.common.interrupt.intx {
|
||||
if !intx.enabled {
|
||||
if let Some(eventfd) = intx.interrupt_source_group.notifier(0) {
|
||||
self.device
|
||||
.enable_irq(VFIO_PCI_INTX_IRQ_INDEX, vec![&eventfd])
|
||||
.map_err(VfioPciError::EnableIntx)?;
|
||||
|
||||
intx.enabled = true;
|
||||
} else {
|
||||
return Err(VfioPciError::MissingNotifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_intx(&mut self) {
|
||||
if let Some(intx) = &mut self.common.interrupt.intx {
|
||||
if intx.enabled {
|
||||
if let Err(e) = self.device.disable_irq(VFIO_PCI_INTX_IRQ_INDEX) {
|
||||
error!("Could not disable INTx: {}", e);
|
||||
} else {
|
||||
intx.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_msi(&self) -> Result<()> {
|
||||
if let Some(msi) = &self.common.interrupt.msi {
|
||||
let mut irq_fds: Vec<EventFd> = Vec::new();
|
||||
for i in 0..msi.cfg.num_enabled_vectors() {
|
||||
if let Some(eventfd) = msi.interrupt_source_group.notifier(i as InterruptIndex) {
|
||||
irq_fds.push(eventfd);
|
||||
} else {
|
||||
return Err(VfioPciError::MissingNotifier);
|
||||
}
|
||||
}
|
||||
|
||||
self.device
|
||||
.enable_msi(irq_fds.iter().collect())
|
||||
.map_err(VfioPciError::EnableMsi)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_msi(&self) {
|
||||
if let Err(e) = self.device.disable_msi() {
|
||||
error!("Could not disable MSI: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_msix(&self) -> Result<()> {
|
||||
if let Some(msix) = &self.common.interrupt.msix {
|
||||
let mut irq_fds: Vec<EventFd> = Vec::new();
|
||||
for i in 0..msix.bar.table_entries.len() {
|
||||
if let Some(eventfd) = msix.interrupt_source_group.notifier(i as InterruptIndex) {
|
||||
irq_fds.push(eventfd);
|
||||
} else {
|
||||
return Err(VfioPciError::MissingNotifier);
|
||||
}
|
||||
}
|
||||
|
||||
self.device
|
||||
.enable_msix(irq_fds.iter().collect())
|
||||
.map_err(VfioPciError::EnableMsix)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_msix(&self) {
|
||||
if let Err(e) = self.device.disable_msix() {
|
||||
error!("Could not disable MSI-X: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_legacy_interrupt(
|
||||
&mut self,
|
||||
legacy_interrupt_group: Option<Arc<dyn InterruptSourceGroup>>,
|
||||
) -> Result<()> {
|
||||
if let Some(irq_info) = self.device.get_irq_info(VFIO_PCI_INTX_IRQ_INDEX) {
|
||||
if irq_info.count == 0 {
|
||||
// A count of 0 means the INTx IRQ is not supported, therefore
|
||||
// it shouldn't be initialized.
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(interrupt_source_group) = legacy_interrupt_group {
|
||||
self.common.interrupt.intx = Some(VfioIntx {
|
||||
interrupt_source_group,
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
self.enable_intx()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_msi_capabilities(&mut self, offset: u64, data: &[u8]) -> Result<()> {
|
||||
match self.common.interrupt.update_msi(offset, data) {
|
||||
Some(InterruptUpdateAction::EnableMsi) => {
|
||||
// Disable INTx before we can enable MSI
|
||||
self.disable_intx();
|
||||
self.enable_msi()?;
|
||||
}
|
||||
Some(InterruptUpdateAction::DisableMsi) => {
|
||||
// Fallback onto INTx when disabling MSI
|
||||
self.disable_msi();
|
||||
self.enable_intx()?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_msix_capabilities(&mut self, offset: u64, data: &[u8]) -> Result<()> {
|
||||
match self.common.interrupt.update_msix(offset, data) {
|
||||
Some(InterruptUpdateAction::EnableMsix) => {
|
||||
// Disable INTx before we can enable MSI-X
|
||||
self.disable_intx();
|
||||
self.enable_msix()?;
|
||||
}
|
||||
Some(InterruptUpdateAction::DisableMsix) => {
|
||||
// Fallback onto INTx when disabling MSI-X
|
||||
self.disable_msix();
|
||||
self.enable_intx()?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_region(&self, addr: u64) -> Option<MmioRegion> {
|
||||
for region in self.common.mmio_regions.iter() {
|
||||
if addr >= region.start.raw_value()
|
||||
@ -972,18 +1022,18 @@ impl Drop for VfioPciDevice {
|
||||
|
||||
if let Some(msix) = &self.common.interrupt.msix {
|
||||
if msix.bar.enabled() {
|
||||
self.disable_msix();
|
||||
self.common.disable_msix(&self.vfio_wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(msi) = &self.common.interrupt.msi {
|
||||
if msi.cfg.enabled() {
|
||||
self.disable_msi();
|
||||
self.common.disable_msi(&self.vfio_wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
if self.common.interrupt.intx_in_use() {
|
||||
self.disable_intx();
|
||||
self.common.disable_intx(&self.vfio_wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1064,12 +1114,18 @@ impl PciDevice for VfioPciDevice {
|
||||
let cap_offset: u64 = reg - cap_base + offset;
|
||||
match cap_id {
|
||||
PciCapabilityId::MessageSignalledInterrupts => {
|
||||
if let Err(e) = self.update_msi_capabilities(cap_offset, data) {
|
||||
if let Err(e) =
|
||||
self.common
|
||||
.update_msi_capabilities(cap_offset, data, &self.vfio_wrapper)
|
||||
{
|
||||
error!("Could not update MSI capabilities: {}", e);
|
||||
}
|
||||
}
|
||||
PciCapabilityId::MsiX => {
|
||||
if let Err(e) = self.update_msix_capabilities(cap_offset, data) {
|
||||
if let Err(e) =
|
||||
self.common
|
||||
.update_msix_capabilities(cap_offset, data, &self.vfio_wrapper)
|
||||
{
|
||||
error!("Could not update MSI-X capabilities: {}", e);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user