summary refs log tree commit diff
path: root/devices/src/pci
diff options
context:
space:
mode:
authorXiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>2019-04-23 17:15:21 +0800
committerCommit Bot <commit-bot@chromium.org>2019-10-29 11:10:47 +0000
commit4b5bb3a4ed3656784172cfabfdcd0d2bbb2b4717 (patch)
tree084c2ed952fec8903e6c086e3f3091c2e7ae3918 /devices/src/pci
parentee723d5204f8a0741cf993900fb6471202db9a97 (diff)
downloadcrosvm-4b5bb3a4ed3656784172cfabfdcd0d2bbb2b4717.tar
crosvm-4b5bb3a4ed3656784172cfabfdcd0d2bbb2b4717.tar.gz
crosvm-4b5bb3a4ed3656784172cfabfdcd0d2bbb2b4717.tar.bz2
crosvm-4b5bb3a4ed3656784172cfabfdcd0d2bbb2b4717.tar.lz
crosvm-4b5bb3a4ed3656784172cfabfdcd0d2bbb2b4717.tar.xz
crosvm-4b5bb3a4ed3656784172cfabfdcd0d2bbb2b4717.tar.zst
crosvm-4b5bb3a4ed3656784172cfabfdcd0d2bbb2b4717.zip
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>
Diffstat (limited to 'devices/src/pci')
-rw-r--r--devices/src/pci/vfio_pci.rs152
1 files changed, 133 insertions, 19 deletions
diff --git a/devices/src/pci/vfio_pci.rs b/devices/src/pci/vfio_pci.rs
index af70aef..a1e7a74 100644
--- a/devices/src/pci/vfio_pci.rs
+++ b/devices/src/pci/vfio_pci.rs
@@ -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);
     }