vfio: Add vfio msi routing information into kvm

When vfio device msi is enabled, use VmIrqRequest->AllocateOneMsi() to
allocate one gsi for a msi vector, and link gsi with irqfd through
vm->register_irqfd, use VmIrqRequest->AddMsiRoute() to add msi routing
info into kvm route table.

BUG=chromium:992270
TEST=none

Change-Id: I5e2d2347e5e26f0ef6e12554dae4b12934b65e82
Signed-off-by: Xiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1581146
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Xiong Zhang 2019-04-23 17:15:21 +08:00 committed by Commit Bot
parent ee723d5204
commit 4b5bb3a4ed
2 changed files with 138 additions and 21 deletions

View file

@ -7,10 +7,12 @@ use std::sync::Arc;
use std::u32;
use kvm::Datamatch;
use msg_socket::{MsgReceiver, MsgSender};
use resources::{Alloc, SystemAllocator};
use sys_util::{error, EventFd};
use vfio_sys::*;
use vm_control::{MaybeOwnedFd, VmIrqRequest, VmIrqRequestSocket, VmIrqResponse};
use crate::pci::pci_device::{Error as PciDeviceError, PciDevice};
use crate::pci::PciInterruptPin;
@ -109,10 +111,13 @@ struct VfioMsiCap {
ctl: u16,
address: u64,
data: u16,
vm_socket_irq: VmIrqRequestSocket,
irqfd: Option<EventFd>,
gsi: Option<u32>,
}
impl VfioMsiCap {
fn new(config: &VfioPciConfig) -> Option<Self> {
fn new(config: &VfioPciConfig, vm_socket_irq: VmIrqRequestSocket) -> Option<Self> {
// msi minimum size is 0xa
let mut msi_len: u32 = MSI_LENGTH_32BIT;
let mut cap_next: u32 = config.read_config_byte(PCI_CAPABILITY_LIST).into();
@ -133,6 +138,9 @@ impl VfioMsiCap {
ctl: 0,
address: 0,
data: 0,
vm_socket_irq,
irqfd: None,
gsi: None,
});
}
let offset = cap_next + PCI_MSI_NEXT_POINTER;
@ -157,6 +165,8 @@ impl VfioMsiCap {
let len = data.len();
let offset = index as u32 - self.offset;
let mut ret: Option<VfioMsiChange> = None;
let old_address = self.address;
let old_data = self.data;
// write msi ctl
if len == 2 && offset == PCI_MSI_FLAGS {
@ -165,6 +175,7 @@ impl VfioMsiCap {
self.ctl = u16::from_le_bytes(value);
let is_enabled = self.is_msi_enabled();
if !was_enabled && is_enabled {
self.enable();
ret = Some(VfioMsiChange::Enable);
} else if was_enabled && !is_enabled {
ret = Some(VfioMsiChange::Disable)
@ -199,12 +210,79 @@ impl VfioMsiCap {
self.data = u16::from_le_bytes(value);
}
if self.is_msi_enabled() && (old_address != self.address || old_data != self.data) {
self.add_msi_route();
}
ret
}
fn is_msi_enabled(&self) -> bool {
self.ctl & PCI_MSI_FLAGS_ENABLE == PCI_MSI_FLAGS_ENABLE
}
fn add_msi_route(&self) {
let gsi = match self.gsi {
Some(g) => g,
None => {
error!("Add msi route but gsi is none");
return;
}
};
if let Err(e) = self.vm_socket_irq.send(&VmIrqRequest::AddMsiRoute {
gsi,
msi_address: self.address,
msi_data: self.data.into(),
}) {
error!("failed to send AddMsiRoute request at {:?}", e);
return;
}
match self.vm_socket_irq.recv() {
Ok(VmIrqResponse::Err(e)) => error!("failed to call AddMsiRoute request {:?}", e),
Ok(_) => {}
Err(e) => error!("failed to receive AddMsiRoute response {:?}", e),
}
}
fn allocate_one_msi(&mut self) {
if self.irqfd.is_none() {
match EventFd::new() {
Ok(fd) => self.irqfd = Some(fd),
Err(e) => {
error!("failed to create eventfd: {:?}", e);
return;
}
};
}
if let Err(e) = self.vm_socket_irq.send(&VmIrqRequest::AllocateOneMsi {
irqfd: MaybeOwnedFd::Borrowed(self.irqfd.as_ref().unwrap().as_raw_fd()),
}) {
error!("failed to send AllocateOneMsi request: {:?}", e);
return;
}
match self.vm_socket_irq.recv() {
Ok(VmIrqResponse::AllocateOneMsi { gsi }) => self.gsi = Some(gsi),
_ => error!("failed to receive AllocateOneMsi Response"),
}
}
fn enable(&mut self) {
if self.gsi.is_none() || self.irqfd.is_none() {
self.allocate_one_msi();
}
self.add_msi_route();
}
fn get_msi_irqfd(&self) -> Option<&EventFd> {
self.irqfd.as_ref()
}
fn get_vm_socket(&self) -> RawFd {
self.vm_socket_irq.as_ref().as_raw_fd()
}
}
struct MmioInfo {
@ -232,10 +310,10 @@ pub struct VfioPciDevice {
impl VfioPciDevice {
/// Constructs a new Vfio Pci device for the give Vfio device
pub fn new(device: VfioDevice) -> Self {
pub fn new(device: VfioDevice, vfio_device_socket_irq: VmIrqRequestSocket) -> Self {
let dev = Arc::new(device);
let config = VfioPciConfig::new(Arc::clone(&dev));
let msi_cap = VfioMsiCap::new(&config);
let msi_cap = VfioMsiCap::new(&config, vfio_device_socket_irq);
VfioPciDevice {
device: dev,
@ -302,6 +380,47 @@ impl VfioPciDevice {
}
self.irq_type = None;
}
fn enable_msi(&mut self) {
if let Some(irq_type) = &self.irq_type {
match irq_type {
VfioIrqType::Intx => self.disable_intx(),
_ => return,
}
}
let irqfd = match &self.msi_cap {
Some(cap) => {
if let Some(fd) = cap.get_msi_irqfd() {
fd
} else {
self.enable_intx();
return;
}
}
None => {
self.enable_intx();
return;
}
};
if let Err(e) = self.device.irq_enable(irqfd, VfioIrqType::Msi) {
error!("failed to enable msi: {}", e);
self.enable_intx();
return;
}
self.irq_type = Some(VfioIrqType::Msi);
}
fn disable_msi(&mut self) {
if let Err(e) = self.device.irq_disable(VfioIrqType::Msi) {
error!("failed to disable msi: {}", e);
return;
}
self.enable_intx();
}
}
impl PciDevice for VfioPciDevice {
@ -321,6 +440,9 @@ impl PciDevice for VfioPciDevice {
if let Some(ref interrupt_resample_evt) = self.interrupt_resample_evt {
fds.push(interrupt_resample_evt.as_raw_fd());
}
if let Some(msi_cap) = &self.msi_cap {
fds.push(msi_cap.get_vm_socket());
}
fds
}
@ -462,27 +584,19 @@ impl PciDevice for VfioPciDevice {
fn write_config_register(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
let start = (reg_idx * 4) as u64 + offset;
let mut msi_change: Option<VfioMsiChange> = None;
if let Some(msi_cap) = self.msi_cap.as_mut() {
if msi_cap.is_msi_reg(start, data.len()) {
if let Some(ref interrupt_evt) = self.interrupt_evt {
match msi_cap.write_msi_reg(start, data) {
Some(VfioMsiChange::Enable) => {
if let Err(e) = self.device.irq_enable(interrupt_evt, VfioIrqType::Msi)
{
error!("{}", e);
}
}
Some(VfioMsiChange::Disable) => {
if let Err(e) = self.device.irq_disable(VfioIrqType::Msi) {
error!("{}", e);
}
}
None => (),
}
}
msi_change = msi_cap.write_msi_reg(start, data);
}
}
match msi_change {
Some(VfioMsiChange::Enable) => self.enable_msi(),
Some(VfioMsiChange::Disable) => self.disable_msi(),
None => (),
}
self.device
.region_write(VFIO_PCI_CONFIG_REGION_INDEX, data, start);
}

View file

@ -258,7 +258,6 @@ type Result<T> = std::result::Result<T, Error>;
enum TaggedControlSocket {
Vm(VmControlResponseSocket),
VmMemory(VmMemoryControlResponseSocket),
#[allow(dead_code)]
VmIrq(VmIrqResponseSocket),
}
@ -1000,10 +999,14 @@ fn create_devices(
pci_devices.push((usb_controller, simple_jail(&cfg, "xhci.policy")?));
if cfg.vfio.is_some() {
let (vfio_host_socket_irq, vfio_device_socket_irq) =
msg_socket::pair::<VmIrqResponse, VmIrqRequest>().map_err(Error::CreateSocket)?;
control_sockets.push(TaggedControlSocket::VmIrq(vfio_host_socket_irq));
let vfio_path = cfg.vfio.as_ref().unwrap().as_path();
let vfiodevice =
VfioDevice::new(vfio_path, vm, mem.clone()).map_err(Error::CreateVfioDevice)?;
let vfiopcidevice = Box::new(VfioPciDevice::new(vfiodevice));
let vfiopcidevice = Box::new(VfioPciDevice::new(vfiodevice, vfio_device_socket_irq));
pci_devices.push((vfiopcidevice, simple_jail(&cfg, "vfio_device.policy")?));
}