diff --git a/pci/src/lib.rs b/pci/src/lib.rs index dabd2280d..b10c7756a 100644 --- a/pci/src/lib.rs +++ b/pci/src/lib.rs @@ -32,7 +32,7 @@ use kvm_ioctls::*; use std::collections::HashMap; use std::io; use std::mem::size_of; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use vm_allocator::SystemAllocator; use vmm_sys_util::eventfd::EventFd; @@ -89,11 +89,11 @@ pub fn vec_with_array_field(count: usize) -> Vec { } pub fn set_kvm_routes( - vm_fd: VmFd, - gsi_msi_routes: Arc>>, + vm_fd: &Arc, + gsi_msi_routes: &HashMap, ) -> Result<(), Error> { let mut entry_vec: Vec = Vec::new(); - for (_, entry) in gsi_msi_routes.lock().unwrap().iter() { + for (_, entry) in gsi_msi_routes.iter() { entry_vec.push(*entry); } @@ -114,8 +114,8 @@ pub fn set_kvm_routes( } pub struct InterruptRoute { - gsi: u32, - irq_fd: EventFd, + pub gsi: u32, + pub irq_fd: EventFd, } impl InterruptRoute { diff --git a/pci/src/msix.rs b/pci/src/msix.rs index 390a3b75b..ba0e027cf 100644 --- a/pci/src/msix.rs +++ b/pci/src/msix.rs @@ -9,9 +9,9 @@ extern crate vm_memory; use std::sync::Arc; use crate::device::InterruptParameters; -use crate::{InterruptDelivery, InterruptRoute, PciCapability, PciCapabilityID}; +use crate::{set_kvm_routes, InterruptDelivery, InterruptRoute, PciCapability, PciCapabilityID}; use byteorder::{ByteOrder, LittleEndian}; -use kvm_bindings::kvm_irq_routing_entry; +use kvm_bindings::{kvm_irq_routing_entry, KVM_IRQ_ROUTING_MSI}; use kvm_ioctls::VmFd; use std::collections::HashMap; use std::sync::Mutex; @@ -57,8 +57,8 @@ pub struct MsixConfig { pub table_entries: Vec, pub pba_entries: Vec, pub irq_routes: Vec, - _vm_fd: Arc, - _gsi_msi_routes: Arc>>, + vm_fd: Arc, + gsi_msi_routes: Arc>>, interrupt_cb: Option>, masked: bool, enabled: bool, @@ -88,8 +88,8 @@ impl MsixConfig { table_entries, pba_entries, irq_routes, - _vm_fd: vm_fd, - _gsi_msi_routes: gsi_msi_routes, + vm_fd, + gsi_msi_routes, interrupt_cb: None, masked: false, enabled: false, @@ -110,10 +110,45 @@ impl MsixConfig { pub fn set_msg_ctl(&mut self, reg: u16) { let old_masked = self.masked; + let old_enabled = self.enabled; self.masked = ((reg >> FUNCTION_MASK_BIT) & 1u16) == 1u16; self.enabled = ((reg >> MSIX_ENABLE_BIT) & 1u16) == 1u16; + // Update KVM routes + if old_masked != self.masked || old_enabled != self.enabled { + let mut gsi_msi_routes = self.gsi_msi_routes.lock().unwrap(); + if self.enabled && !self.masked { + for (idx, table_entry) in self.table_entries.iter().enumerate() { + // Ignore MSI-X vector if masked. + if table_entry.masked() { + continue; + } + + let gsi = self.irq_routes[idx].gsi; + + let mut entry = kvm_irq_routing_entry { + gsi, + type_: KVM_IRQ_ROUTING_MSI, + ..Default::default() + }; + + entry.u.msi.address_lo = table_entry.msg_addr_lo; + entry.u.msi.address_hi = table_entry.msg_addr_hi; + entry.u.msi.data = table_entry.msg_data; + + gsi_msi_routes.insert(gsi, entry); + } + } else { + for route in self.irq_routes.iter() { + gsi_msi_routes.remove(&route.gsi); + } + } + if let Err(e) = set_kvm_routes(&self.vm_fd, &gsi_msi_routes) { + error!("Failed updating KVM routes: {:?}", e); + } + } + // If the Function Mask bit was set, and has just been cleared, it's // important to go through the entire PBA to check if there was any // pending MSI-X message to inject, given that the vector is not @@ -219,6 +254,31 @@ impl MsixConfig { _ => error!("invalid data length"), }; + // Update interrupt routes + if self.enabled && !self.masked { + let mut gsi_msi_routes = self.gsi_msi_routes.lock().unwrap(); + let table_entry = &self.table_entries[index]; + let gsi = self.irq_routes[index].gsi; + if !table_entry.masked() { + let mut entry = kvm_irq_routing_entry { + gsi, + type_: KVM_IRQ_ROUTING_MSI, + ..Default::default() + }; + + entry.u.msi.address_lo = table_entry.msg_addr_lo; + entry.u.msi.address_hi = table_entry.msg_addr_hi; + entry.u.msi.data = table_entry.msg_data; + + gsi_msi_routes.insert(gsi, entry); + } else { + gsi_msi_routes.remove(&gsi); + } + if let Err(e) = set_kvm_routes(&self.vm_fd, &gsi_msi_routes) { + error!("Failed updating KVM routes: {:?}", e); + } + } + // After the MSI-X table entry has been updated, it is necessary to // check if the vector control masking bit has changed. In case the // bit has been flipped from 1 to 0, we need to inject a MSI message diff --git a/vfio/src/vfio_pci.rs b/vfio/src/vfio_pci.rs index 16802de79..3f7c5c001 100644 --- a/vfio/src/vfio_pci.rs +++ b/vfio/src/vfio_pci.rs @@ -113,14 +113,14 @@ struct VfioMsix { impl VfioMsix { fn update(&mut self, offset: u64, data: &[u8]) -> Option { - let old_enabled = self.cap.enabled(); + let old_enabled = self.bar.enabled(); // Update "Message Control" word if offset == 2 && data.len() == 2 { - self.cap.set_msg_ctl(LittleEndian::read_u16(data)); + self.bar.set_msg_ctl(LittleEndian::read_u16(data)); } - let new_enabled = self.cap.enabled(); + let new_enabled = self.bar.enabled(); if !old_enabled && new_enabled { return Some(InterruptUpdateAction::EnableMsix); @@ -507,53 +507,12 @@ impl VfioPciDevice { self.set_kvm_routes() } - fn update_msix_interrupt_routes(&self, msix: &VfioMsix) -> Result<()> { - if msix.cap.enabled() && !msix.cap.masked() { - let mut gsi_msi_routes = self.gsi_msi_routes.lock().unwrap(); - - for (idx, table_entry) in msix.bar.table_entries.iter().enumerate() { - // Ignore MSI-X vector if masked. - if table_entry.masked() { - continue; - } - - let gsi = self.interrupt_routes[idx].gsi; - - let mut entry = kvm_irq_routing_entry { - gsi, - type_: KVM_IRQ_ROUTING_MSI, - ..Default::default() - }; - - entry.u.msi.address_lo = table_entry.msg_addr_lo; - entry.u.msi.address_hi = table_entry.msg_addr_hi; - entry.u.msi.data = table_entry.msg_data; - - gsi_msi_routes.insert(gsi, entry); - } - } else { - let mut gsi_msi_routes = self.gsi_msi_routes.lock().unwrap(); - - for route in self.interrupt_routes.iter() { - gsi_msi_routes.remove(&route.gsi); - } - } - - self.set_kvm_routes() - } - fn read_msix_table(&mut self, offset: u64, data: &mut [u8]) { self.interrupt.msix_read_table(offset, data); } fn write_msix_table(&mut self, offset: u64, data: &[u8]) { self.interrupt.msix_write_table(offset, data); - - if let Some(msix) = &self.interrupt.msix { - if let Err(e) = self.update_msix_interrupt_routes(&msix) { - error!("Could not update MSI-X interrupt routes: {}", e); - } - } } fn update_msi_capabilities(&mut self, offset: u64, data: &[u8]) -> Result<()> { @@ -610,14 +569,7 @@ impl VfioPciDevice { _ => {} } - // Update the gsi_msi_routes table because the state of the enable bit - // changed. - if let Some(msix) = &self.interrupt.msix { - return self.update_msix_interrupt_routes(&msix); - } - - // If the code reach this point, something went wrong. - Err(VfioPciError::MsixNotConfigured) + Ok(()) } fn find_region(&self, addr: u64) -> Option { @@ -734,7 +686,7 @@ impl Drop for VfioPciDevice { self.unmap_mmio_regions(); if let Some(msix) = &self.interrupt.msix { - if msix.cap.enabled() && self.device.disable_msix().is_err() { + if msix.bar.enabled() && self.device.disable_msix().is_err() { error!("Could not disable MSI-X"); } }