vmm: Add MSI-X support to virtio-pci devices

In order to allow virtio-pci devices to use MSI-X messages instead
of legacy pin based interrupts, this patch implements the MSI-X
support for cloud-hypervisor. The VMM code and virtio-pci bits have
been modified based on the "msix" module previously added to the pci
crate.

Fixes #12

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf
2019-05-29 16:33:29 -07:00
committed by Rob Bradford
parent 13a065d2cd
commit 8df05b72dc
6 changed files with 148 additions and 36 deletions

View File

@@ -218,6 +218,9 @@ pub struct Queue {
/// Inidcates if the queue is finished with configuration
pub ready: bool,
/// Interrupt vector index of the queue
pub vector: u16,
/// Guest physical address of the descriptor table
pub desc_table: GuestAddress,
@@ -238,6 +241,7 @@ impl Queue {
max_size,
size: max_size,
ready: false,
vector: 0,
desc_table: GuestAddress(0),
avail_ring: GuestAddress(0),
used_ring: GuestAddress(0),

View File

@@ -40,6 +40,7 @@ pub struct VirtioPciCommonConfig {
pub device_feature_select: u32,
pub driver_feature_select: u32,
pub queue_select: u16,
pub msix_config: u16,
}
impl VirtioPciCommonConfig {
@@ -119,10 +120,11 @@ impl VirtioPciCommonConfig {
fn read_common_config_word(&self, offset: u64, queues: &[Queue]) -> u16 {
debug!("read_common_config_word: offset 0x{:x}", offset);
match offset {
0x10 => 0, // TODO msi-x (crbug/854765): self.msix_config,
0x10 => self.msix_config,
0x12 => queues.len() as u16, // num_queues
0x16 => self.queue_select,
0x18 => self.with_queue(queues, |q| q.size).unwrap_or(0),
0x1a => self.with_queue(queues, |q| q.vector).unwrap_or(0),
0x1c => {
if self.with_queue(queues, |q| q.ready).unwrap_or(false) {
1
@@ -141,10 +143,10 @@ impl VirtioPciCommonConfig {
fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut Vec<Queue>) {
debug!("write_common_config_word: offset 0x{:x}", offset);
match offset {
0x10 => (), // TODO msi-x (crbug/854765): self.msix_config = value,
0x10 => self.msix_config = value,
0x16 => self.queue_select = value,
0x18 => self.with_queue_mut(queues, |q| q.size = value),
0x1a => (), // TODO msi-x (crbug/854765): self.with_queue_mut(queues, |q| q.msix_vector = v),
0x1a => self.with_queue_mut(queues, |q| q.vector = value),
0x1c => self.with_queue_mut(queues, |q| q.ready = value == 1),
_ => {
warn!("invalid virtio register word write: 0x{:x}", offset);

View File

@@ -16,11 +16,13 @@ use byteorder::{ByteOrder, LittleEndian};
use libc::EFD_NONBLOCK;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::sync::Mutex;
use devices::BusDevice;
use pci::{
IrqClosure, PciBarConfiguration, PciCapability, PciCapabilityID, PciClassCode,
PciConfiguration, PciDevice, PciDeviceError, PciHeaderType, PciInterruptPin, PciSubclass,
IrqClosure, MsixCap, MsixClosure, MsixConfig, PciBarConfiguration, PciCapability,
PciCapabilityID, PciClassCode, PciConfiguration, PciDevice, PciDeviceError, PciHeaderType,
PciInterruptPin, PciSubclass,
};
use vm_allocator::SystemAllocator;
use vm_memory::{Address, ByteValued, GuestAddress, GuestMemoryMmap, GuestUsize, Le32};
@@ -143,7 +145,11 @@ const DEVICE_CONFIG_BAR_OFFSET: u64 = 0x2000;
const DEVICE_CONFIG_SIZE: u64 = 0x1000;
const NOTIFICATION_BAR_OFFSET: u64 = 0x3000;
const NOTIFICATION_SIZE: u64 = 0x1000;
const CAPABILITY_BAR_SIZE: u64 = 0x4000;
const MSIX_TABLE_BAR_OFFSET: u64 = 0x6000;
const MSIX_TABLE_SIZE: u64 = 0x1000;
const MSIX_PBA_BAR_OFFSET: u64 = 0x7000;
const MSIX_PBA_SIZE: u64 = 0x1000;
const CAPABILITY_BAR_SIZE: u64 = 0x8000;
const NOTIFY_OFF_MULTIPLIER: u32 = 4; // A dword per notification address.
@@ -157,6 +163,12 @@ pub struct VirtioPciDevice {
// virtio PCI common configuration
common_config: VirtioPciCommonConfig,
// MSI-X config
msix_config: Arc<Mutex<MsixConfig>>,
// Number of MSI-X vectors
msix_num: u16,
// Virtio device reference and status
device: Box<VirtioDevice>,
device_activated: bool,
@@ -178,7 +190,7 @@ pub struct VirtioPciDevice {
impl VirtioPciDevice {
/// Constructs a new PCI transport for the given virtio device.
pub fn new(memory: GuestMemoryMmap, device: Box<VirtioDevice>) -> Result<Self> {
pub fn new(memory: GuestMemoryMmap, device: Box<VirtioDevice>, msix_num: u16) -> Result<Self> {
let mut queue_evts = Vec::new();
for _ in device.queue_max_sizes().iter() {
queue_evts.push(EventFd::new(EFD_NONBLOCK)?)
@@ -210,7 +222,10 @@ impl VirtioPciDevice {
device_feature_select: 0,
driver_feature_select: 0,
queue_select: 0,
msix_config: 0,
},
msix_config: Arc::new(Mutex::new(MsixConfig::new(msix_num))),
msix_num,
device,
device_activated: false,
interrupt_status: Arc::new(AtomicUsize::new(0)),
@@ -302,13 +317,23 @@ impl VirtioPciDevice {
.add_capability(&configuration_cap)
.map_err(PciDeviceError::CapabilitiesSetup)?;
let msix_cap = MsixCap::new(
settings_bar,
self.msix_num,
MSIX_TABLE_BAR_OFFSET as u32,
MSIX_PBA_BAR_OFFSET as u32,
);
self.configuration
.add_capability(&msix_cap)
.map_err(PciDeviceError::CapabilitiesSetup)?;
self.settings_bar = settings_bar;
Ok(())
}
}
impl PciDevice for VirtioPciDevice {
fn assign_irq(&mut self, irq_cb: Arc<IrqClosure>, irq_num: u32, irq_pin: PciInterruptPin) {
fn assign_pin_irq(&mut self, irq_cb: Arc<IrqClosure>, irq_num: u32, irq_pin: PciInterruptPin) {
self.configuration.set_irq(irq_num as u8, irq_pin);
let cb = Arc::new(Box::new(move |_queue: &Queue| (irq_cb)()) as VirtioInterrupt);
@@ -316,6 +341,16 @@ impl PciDevice for VirtioPciDevice {
self.interrupt_cb = Some(cb);
}
fn assign_msix(&mut self, msi_cb: Arc<MsixClosure>) {
let msix_config = self.msix_config.clone();
let cb = Arc::new(Box::new(move |queue: &Queue| {
(msi_cb)(msix_config.lock().unwrap().table_entries[queue.vector as usize].clone())
}) as VirtioInterruptClosure);
self.interrupt_cb = Some(cb);
}
fn config_registers(&self) -> &PciConfiguration {
&self.configuration
}
@@ -406,6 +441,18 @@ impl PciDevice for VirtioPciDevice {
{
// Handled with ioeventfds.
}
o if MSIX_TABLE_BAR_OFFSET <= o && o < MSIX_TABLE_BAR_OFFSET + MSIX_TABLE_SIZE => {
self.msix_config
.lock()
.unwrap()
.read_table(o - MSIX_TABLE_BAR_OFFSET, data);
}
o if MSIX_PBA_BAR_OFFSET <= o && o < MSIX_PBA_BAR_OFFSET + MSIX_PBA_SIZE => {
self.msix_config
.lock()
.unwrap()
.read_pba(o - MSIX_PBA_BAR_OFFSET, data);
}
_ => (),
}
}
@@ -434,6 +481,18 @@ impl PciDevice for VirtioPciDevice {
{
// Handled with ioeventfds.
}
o if MSIX_TABLE_BAR_OFFSET <= o && o < MSIX_TABLE_BAR_OFFSET + MSIX_TABLE_SIZE => {
self.msix_config
.lock()
.unwrap()
.write_table(o - MSIX_TABLE_BAR_OFFSET, data);
}
o if MSIX_PBA_BAR_OFFSET <= o && o < MSIX_PBA_BAR_OFFSET + MSIX_PBA_SIZE => {
self.msix_config
.lock()
.unwrap()
.write_pba(o - MSIX_PBA_BAR_OFFSET, data);
}
_ => (),
};