pci: Improve MSI-X code to let VFIO rely on it

This commit enhances the current msi-x code hosted in the pci crate
in order to be reused by the vfio crate. Specifically, it creates
several useful methods for the MsixCap structure that can simplify
the caller's code.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2019-07-16 17:19:21 -07:00
parent 29878956bd
commit 72007f016a
3 changed files with 60 additions and 16 deletions

View File

@ -24,7 +24,7 @@ pub use self::configuration::{
pub use self::device::{
Error as PciDeviceError, InterruptDelivery, InterruptParameters, PciDevice,
};
pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry};
pub use self::msix::{MsixCap, MsixConfig, MsixTableEntry, MSIX_TABLE_ENTRY_SIZE};
/// PCI has four interrupt pins A->D.
#[derive(Copy, Clone)]

View File

@ -18,6 +18,10 @@ const MSIX_TABLE_ENTRIES_MODULO: u64 = 16;
const MSIX_PBA_ENTRIES_MODULO: u64 = 8;
const BITS_PER_PBA_ENTRY: usize = 64;
const FUNCTION_MASK_BIT: u8 = 14;
const MSIX_ENABLE_BIT: u8 = 15;
const FUNCTION_MASK_MASK: u16 = (1 << FUNCTION_MASK_BIT) as u16;
const MSIX_ENABLE_MASK: u16 = (1 << MSIX_ENABLE_BIT) as u16;
pub const MSIX_TABLE_ENTRY_SIZE: usize = 16;
#[derive(Debug, Clone)]
pub struct MsixTableEntry {
@ -28,8 +32,8 @@ pub struct MsixTableEntry {
}
impl MsixTableEntry {
pub fn is_masked(&self) -> bool {
self.vector_ctl == 1u32
pub fn masked(&self) -> bool {
self.vector_ctl & 0x1 == 0x1
}
}
@ -73,7 +77,7 @@ impl MsixConfig {
self.interrupt_cb = Some(cb);
}
pub fn is_masked(&self) -> bool {
pub fn masked(&self) -> bool {
self.masked
}
@ -88,14 +92,14 @@ impl MsixConfig {
// masked.
if old_masked && !self.masked {
for (index, entry) in self.table_entries.clone().iter().enumerate() {
if !entry.is_masked() && self.get_pba_bit(index as u16) == 1 {
if !entry.masked() && self.get_pba_bit(index as u16) == 1 {
self.inject_msix_and_clear_pba(index);
}
}
}
}
pub fn read_table(&mut self, offset: u64, data: &mut [u8]) {
pub fn read_table(&self, offset: u64, data: &mut [u8]) {
assert!((data.len() == 4 || data.len() == 8));
let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize;
@ -196,9 +200,9 @@ impl MsixConfig {
// device.
if let Some(old_entry) = old_entry {
// Check if bit has been flipped
if !self.is_masked()
&& old_entry.is_masked()
&& !self.table_entries[index].is_masked()
if !self.masked()
&& old_entry.masked()
&& !self.table_entries[index].masked()
&& self.get_pba_bit(index as u16) == 1
{
self.inject_msix_and_clear_pba(index);
@ -297,15 +301,15 @@ pub struct MsixCap {
// 13-11: Reserved
// 14: Mask. Mask all MSI-X when set.
// 15: Enable. Enable all MSI-X when set.
msg_ctl: u16,
pub msg_ctl: u16,
// Table. Contains the offset and the BAR indicator (BIR)
// 2-0: Table BAR indicator (BIR). Can be 0 to 5.
// 31-3: Table offset in the BAR pointed by the BIR.
table: u32,
pub table: u32,
// Pending Bit Array. Contains the offset and the BAR indicator (BIR)
// 2-0: PBA BAR indicator (BIR). Can be 0 to 5.
// 31-3: PBA offset in the BAR pointed by the BIR.
pba: u32,
pub pba: u32,
}
// It is safe to implement ByteValued. All members are simple numbers and any value is valid.
@ -322,7 +326,13 @@ impl PciCapability for MsixCap {
}
impl MsixCap {
pub fn new(pci_bar: u8, table_size: u16, table_off: u32, pba_off: u32) -> Self {
pub fn new(
table_pci_bar: u8,
table_size: u16,
table_off: u32,
pba_pci_bar: u8,
pba_off: u32,
) -> Self {
assert!(table_size < MAX_MSIX_VECTORS_PER_DEVICE);
// Set the table size and enable MSI-X.
@ -330,8 +340,41 @@ impl MsixCap {
MsixCap {
msg_ctl,
table: (table_off & 0xffff_fff8u32) | u32::from(pci_bar & 0x7u8),
pba: (pba_off & 0xffff_fff8u32) | u32::from(pci_bar & 0x7u8),
table: (table_off & 0xffff_fff8u32) | u32::from(table_pci_bar & 0x7u8),
pba: (pba_off & 0xffff_fff8u32) | u32::from(pba_pci_bar & 0x7u8),
}
}
pub fn set_msg_ctl(&mut self, data: u16) {
self.msg_ctl = (self.msg_ctl & !(FUNCTION_MASK_MASK | MSIX_ENABLE_MASK))
| (data & (FUNCTION_MASK_MASK | MSIX_ENABLE_MASK));
}
pub fn masked(&self) -> bool {
(self.msg_ctl >> FUNCTION_MASK_BIT) & 0x1 == 0x1
}
pub fn enabled(&self) -> bool {
(self.msg_ctl >> MSIX_ENABLE_BIT) & 0x1 == 0x1
}
pub fn table_offset(&self) -> u32 {
self.table >> 3
}
pub fn pba_offset(&self) -> u32 {
self.pba >> 3
}
pub fn table_bir(&self) -> u32 {
self.table & 0x7
}
pub fn pba_bir(&self) -> u32 {
self.pba & 0x7
}
pub fn table_size(&self) -> u16 {
(self.msg_ctl & 0x7ff) + 1
}
}

View File

@ -354,6 +354,7 @@ impl VirtioPciDevice {
settings_bar,
self.msix_num,
MSIX_TABLE_BAR_OFFSET as u32,
settings_bar,
MSIX_PBA_BAR_OFFSET as u32,
);
self.configuration
@ -401,7 +402,7 @@ impl PciDevice for VirtioPciDevice {
// device should not inject the interrupt.
// Instead, the Pending Bit Array table is updated to reflect there
// is a pending interrupt for this specific vector.
if config.is_masked() || entry.is_masked() {
if config.masked() || entry.masked() {
config.set_pba_bit(queue.vector, false);
return Ok(());
}