msix: Set KVM routes from MsixConfig instead of VFIO

Now that MsixConfig has access to both KVM VmFd and the list of GSI
routes, the update of the KVM GSI routes can be directly done from
MsixConfig instead of specifically from the vfio-pci implementation.

By moving the KVM GSI routes update at the MsixConfig level, both
vfio-pci and virtio-pci (or any other emulated PCI device) can benefit
from it, without having to implement it on their own.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2020-01-09 22:21:21 +01:00 committed by Samuel Ortiz
parent 2381f32ae0
commit 3fe362e3bd
3 changed files with 77 additions and 65 deletions

View File

@ -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<T: Default, F>(count: usize) -> Vec<T> {
}
pub fn set_kvm_routes<S: ::std::hash::BuildHasher>(
vm_fd: VmFd,
gsi_msi_routes: Arc<Mutex<HashMap<u32, kvm_irq_routing_entry, S>>>,
vm_fd: &Arc<VmFd>,
gsi_msi_routes: &HashMap<u32, kvm_irq_routing_entry, S>,
) -> Result<(), Error> {
let mut entry_vec: Vec<kvm_irq_routing_entry> = 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<S: ::std::hash::BuildHasher>(
}
pub struct InterruptRoute {
gsi: u32,
irq_fd: EventFd,
pub gsi: u32,
pub irq_fd: EventFd,
}
impl InterruptRoute {

View File

@ -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<MsixTableEntry>,
pub pba_entries: Vec<u64>,
pub irq_routes: Vec<InterruptRoute>,
_vm_fd: Arc<VmFd>,
_gsi_msi_routes: Arc<Mutex<HashMap<u32, kvm_irq_routing_entry>>>,
vm_fd: Arc<VmFd>,
gsi_msi_routes: Arc<Mutex<HashMap<u32, kvm_irq_routing_entry>>>,
interrupt_cb: Option<Arc<InterruptDelivery>>,
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

View File

@ -113,14 +113,14 @@ struct VfioMsix {
impl VfioMsix {
fn update(&mut self, offset: u64, data: &[u8]) -> Option<InterruptUpdateAction> {
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<MmioRegion> {
@ -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");
}
}