crosvm/pci: update to PCI/INTx allocation

For VFIO devices, allow host interrupt allocation
numbering to match ACPI _CRS.

BUG=b:179648314
TEST=./test_all, boot on sytem with vfio devices connected.

Change-Id: Ied1153f3fc72876d8f3df8822ff6d4e869c40f62
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2893367
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Tomasz Jeznach <tjeznach@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Tomasz Jeznach 2021-04-28 19:44:23 -07:00 committed by Commit Bot
parent 737ff125ca
commit 7ab02aa85b
7 changed files with 136 additions and 107 deletions

View file

@ -343,34 +343,23 @@ pub fn configure_pci_device<V: VmArch, Vcpu: VcpuArch>(
.allocate_device_bars(resources)
.map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
// Do not suggest INTx for hot-plug devices.
let intx_event = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
let intx_resample = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
if let Some((gsi, _pin)) = device.assign_irq(&intx_event, &intx_resample, None) {
resources.reserve_irq(gsi);
linux
.irq_chip
.as_irq_chip_mut()
.register_irq_event(gsi, &intx_event, Some(&intx_resample))
.map_err(DeviceRegistrationError::RegisterIrqfd)?;
}
let mut keep_rds = device.keep_rds();
syslog::push_descriptors(&mut keep_rds);
let irqfd = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
let irq_resample_fd = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
let irq_num = resources
.allocate_irq()
.ok_or(DeviceRegistrationError::AllocateIrq)?;
// Rotate interrupt pins across PCI logical functions.
let pci_irq_pin = match pci_address.func % 4 {
0 => PciInterruptPin::IntA,
1 => PciInterruptPin::IntB,
2 => PciInterruptPin::IntC,
3 => PciInterruptPin::IntD,
_ => unreachable!(), // Obviously not possible, but the compiler is not smart enough.
};
linux
.irq_chip
.as_irq_chip_mut()
.register_irq_event(irq_num, &irqfd, Some(&irq_resample_fd))
.map_err(DeviceRegistrationError::RegisterIrqfd)?;
keep_rds.push(irqfd.as_raw_descriptor());
keep_rds.push(irq_resample_fd.as_raw_descriptor());
device.assign_irq(irqfd, irq_resample_fd, irq_num, pci_irq_pin);
device
.register_device_capabilities()
.map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?;
@ -434,11 +423,8 @@ pub fn generate_pci_root(
DeviceRegistrationError,
> {
let mut root = PciRoot::new(mmio_bus.clone(), io_bus.clone());
let mut pci_irqs = Vec::new();
let mut pid_labels = BTreeMap::new();
let mut irqs: Vec<Option<u32>> = vec![None; max_irqs];
// Allocate PCI device address before allocating BARs.
let mut device_addrs = Vec::<PciAddress>::new();
for (device, _jail) in devices.iter_mut() {
@ -466,14 +452,12 @@ pub fn generate_pci_root(
device_ranges.insert(dev_idx, ranges);
}
for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() {
let address = device_addrs[dev_idx];
let mut keep_rds = device.keep_rds();
syslog::push_descriptors(&mut keep_rds);
keep_rds.append(&mut vm.get_memory().as_raw_descriptors());
// Allocate legacy INTx
let mut pci_irqs = Vec::new();
let mut irqs: Vec<Option<u32>> = vec![None; max_irqs];
let irqfd = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
let irq_resample_fd = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
for (dev_idx, (device, _jail)) in devices.iter_mut().enumerate() {
// For default interrupt routing use next preallocated interrupt from the pool.
let irq_num = if let Some(irq) = irqs[dev_idx % max_irqs] {
irq
} else {
@ -483,23 +467,30 @@ pub fn generate_pci_root(
irqs[dev_idx % max_irqs] = Some(irq);
irq
};
// Rotate interrupt pins across PCI logical functions.
let pci_irq_pin = match address.func % 4 {
0 => PciInterruptPin::IntA,
1 => PciInterruptPin::IntB,
2 => PciInterruptPin::IntC,
3 => PciInterruptPin::IntD,
_ => unreachable!(), // Obviously not possible, but the compiler is not smart enough.
};
irq_chip
.register_irq_event(irq_num, &irqfd, Some(&irq_resample_fd))
.map_err(DeviceRegistrationError::RegisterIrqfd)?;
let intx_event = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
let intx_resample = Event::new().map_err(DeviceRegistrationError::EventCreate)?;
if let Some((gsi, pin)) = device.assign_irq(&intx_event, &intx_resample, Some(irq_num)) {
// reserve INTx if needed and non-default.
if gsi != irq_num {
resources.reserve_irq(gsi);
};
irq_chip
.register_irq_event(gsi, &intx_event, Some(&intx_resample))
.map_err(DeviceRegistrationError::RegisterIrqfd)?;
pci_irqs.push((device_addrs[dev_idx], gsi, pin));
}
}
for (dev_idx, (mut device, jail)) in devices.into_iter().enumerate() {
let address = device_addrs[dev_idx];
let mut keep_rds = device.keep_rds();
syslog::push_descriptors(&mut keep_rds);
keep_rds.append(&mut vm.get_memory().as_raw_descriptors());
keep_rds.push(irqfd.as_raw_descriptor());
keep_rds.push(irq_resample_fd.as_raw_descriptor());
device.assign_irq(irqfd, irq_resample_fd, irq_num, pci_irq_pin);
pci_irqs.push((address, irq_num, pci_irq_pin));
let ranges = io_ranges.remove(&dev_idx).unwrap_or_default();
let device_ranges = device_ranges.remove(&dev_idx).unwrap_or_default();
device
@ -511,6 +502,7 @@ pub fn generate_pci_root(
.map_err(DeviceRegistrationError::RegisterIoevent)?;
keep_rds.push(event.as_raw_descriptor());
}
let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
let proxy = ProxyDevice::new(device, &jail, keep_rds)
.map_err(DeviceRegistrationError::ProxyDeviceCreation)?;

View file

@ -298,14 +298,15 @@ impl PciDevice for Ac97Dev {
fn assign_irq(
&mut self,
irq_evt: Event,
irq_resample_evt: Event,
irq_num: u32,
irq_pin: PciInterruptPin,
) {
self.config_regs.set_irq(irq_num as u8, irq_pin);
self.irq_evt = Some(irq_evt);
self.irq_resample_evt = Some(irq_resample_evt);
irq_evt: &Event,
irq_resample_evt: &Event,
irq_num: Option<u32>,
) -> Option<(u32, PciInterruptPin)> {
self.irq_evt = Some(irq_evt.try_clone().ok()?);
self.irq_resample_evt = Some(irq_resample_evt.try_clone().ok()?);
let gsi = irq_num?;
self.config_regs.set_irq(gsi as u8, PciInterruptPin::IntA);
Some((gsi, PciInterruptPin::IntA))
}
fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {

View file

@ -89,13 +89,15 @@ pub trait PciDevice: Send {
/// Assign a legacy PCI IRQ to this device.
/// The device may write to `irq_evt` to trigger an interrupt.
/// When `irq_resample_evt` is signaled, the device should re-assert `irq_evt` if necessary.
/// Optional irq_num can be used for default INTx allocation, device can overwrite it.
/// If legacy INTx is used, function shall return requested IRQ number and PCI INTx pin.
fn assign_irq(
&mut self,
_irq_evt: Event,
_irq_resample_evt: Event,
_irq_num: u32,
_irq_pin: PciInterruptPin,
) {
_irq_evt: &Event,
_irq_resample_evt: &Event,
_irq_num: Option<u32>,
) -> Option<(u32, PciInterruptPin)> {
None
}
/// Allocates the needed IO BAR space using the `allocate` function which takes a size and
/// returns an address. Returns a Vec of (address, length) tuples.
@ -222,12 +224,11 @@ impl<T: PciDevice + ?Sized> PciDevice for Box<T> {
}
fn assign_irq(
&mut self,
irq_evt: Event,
irq_resample_evt: Event,
irq_num: u32,
irq_pin: PciInterruptPin,
) {
(**self).assign_irq(irq_evt, irq_resample_evt, irq_num, irq_pin)
irq_evt: &Event,
irq_resample_evt: &Event,
irq_num: Option<u32>,
) -> Option<(u32, PciInterruptPin)> {
(**self).assign_irq(irq_evt, irq_resample_evt, irq_num)
}
fn allocate_io_bars(&mut self, resources: &mut SystemAllocator) -> Result<Vec<(u64, u64)>> {
(**self).allocate_io_bars(resources)

View file

@ -109,17 +109,19 @@ impl PciDevice for PciBridge {
fn assign_irq(
&mut self,
irq_evt: Event,
irq_resample_evt: Event,
irq_num: u32,
irq_pin: PciInterruptPin,
) {
self.config.set_irq(irq_num as u8, irq_pin);
self.interrupt_evt = Some(irq_evt);
self.interrupt_resample_evt = Some(irq_resample_evt);
irq_evt: &Event,
irq_resample_evt: &Event,
irq_num: Option<u32>,
) -> Option<(u32, PciInterruptPin)> {
self.interrupt_evt = Some(irq_evt.try_clone().ok()?);
self.interrupt_resample_evt = Some(irq_resample_evt.try_clone().ok()?);
let msix_config_clone = self.msix_config.clone();
self.device.clone_interrupt(msix_config_clone);
let gsi = irq_num?;
self.config.set_irq(gsi as u8, PciInterruptPin::IntA);
Some((gsi, PciInterruptPin::IntA))
}
fn allocate_io_bars(

View file

@ -4,6 +4,8 @@
use std::cmp::{max, min};
use std::collections::BTreeSet;
use std::fs;
use std::path::PathBuf;
use std::sync::Arc;
use std::u32;
@ -35,7 +37,7 @@ const INTEL_VENDOR_ID: u16 = 0x8086;
const PCI_COMMAND: u32 = 0x4;
const PCI_COMMAND_MEMORY: u8 = 0x2;
const PCI_BASE_CLASS_CODE: u32 = 0x0B;
const PCI_INTERRUPT_NUM: u32 = 0x3C;
const PCI_INTERRUPT_PIN: u32 = 0x3D;
struct VfioPciConfig {
@ -932,19 +934,38 @@ impl PciDevice for VfioPciDevice {
fn assign_irq(
&mut self,
irq_evt: Event,
irq_resample_evt: Event,
irq_num: u32,
_irq_pin: PciInterruptPin,
) {
self.config.write_config_byte(irq_num as u8, 0x3C);
self.interrupt_evt = Some(irq_evt);
self.interrupt_resample_evt = Some(irq_resample_evt);
irq_evt: &Event,
irq_resample_evt: &Event,
_irq_num: Option<u32>,
) -> Option<(u32, PciInterruptPin)> {
// Keep event/resample event references.
self.interrupt_evt = Some(irq_evt.try_clone().ok()?);
self.interrupt_resample_evt = Some(irq_resample_evt.try_clone().ok()?);
// Is INTx configured?
let pin = match self.config.read_config_byte(PCI_INTERRUPT_PIN) {
1 => Some(PciInterruptPin::IntA),
2 => Some(PciInterruptPin::IntB),
3 => Some(PciInterruptPin::IntC),
4 => Some(PciInterruptPin::IntD),
_ => None,
}?;
// enable INTX
if self.config.read_config_byte(PCI_INTERRUPT_PIN) > 0 {
self.enable_intx();
}
self.enable_intx();
// TODO: replace sysfs/irq value parsing with vfio interface
// reporting host allocated interrupt number and type.
let mut path = PathBuf::from("/sys/bus/pci/devices");
path.push(self.device.device_name());
path.push("irq");
let gsi = fs::read_to_string(path)
.map(|v| v.trim().parse::<u32>().unwrap_or(0))
.unwrap_or(0);
self.config.write_config_byte(gsi as u8, PCI_INTERRUPT_NUM);
Some((gsi, pin))
}
fn allocate_io_bars(

View file

@ -13,7 +13,7 @@ use crate::usb::xhci::xhci::Xhci;
use crate::usb::xhci::xhci_backend_device_provider::XhciBackendDeviceProvider;
use crate::usb::xhci::xhci_regs::{init_xhci_mmio_space_and_regs, XhciRegs};
use crate::utils::FailHandle;
use base::{error, Event, RawDescriptor};
use base::{error, AsRawDescriptor, Event, RawDescriptor};
use resources::{Alloc, MmioType, SystemAllocator};
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
@ -189,6 +189,16 @@ impl PciDevice for XhciController {
fn keep_rds(&self) -> Vec<RawDescriptor> {
match &self.state {
XhciControllerState::Created { device_provider } => device_provider.keep_rds(),
XhciControllerState::IrqAssigned {
device_provider,
irq_evt,
irq_resample_evt,
} => {
let mut keep_rds = device_provider.keep_rds();
keep_rds.push(irq_evt.as_raw_descriptor());
keep_rds.push(irq_resample_evt.as_raw_descriptor());
keep_rds
}
_ => {
error!("xhci controller is in a wrong state");
vec![]
@ -198,24 +208,25 @@ impl PciDevice for XhciController {
fn assign_irq(
&mut self,
irq_evt: Event,
irq_resample_evt: Event,
irq_num: u32,
irq_pin: PciInterruptPin,
) {
irq_evt: &Event,
irq_resample_evt: &Event,
irq_num: Option<u32>,
) -> Option<(u32, PciInterruptPin)> {
let gsi = irq_num?;
match mem::replace(&mut self.state, XhciControllerState::Unknown) {
XhciControllerState::Created { device_provider } => {
self.config_regs.set_irq(irq_num as u8, irq_pin);
self.config_regs.set_irq(gsi as u8, PciInterruptPin::IntA);
self.state = XhciControllerState::IrqAssigned {
device_provider,
irq_evt,
irq_resample_evt,
irq_evt: irq_evt.try_clone().ok()?,
irq_resample_evt: irq_resample_evt.try_clone().ok()?,
}
}
_ => {
error!("xhci controller is in a wrong state");
}
}
Some((gsi, PciInterruptPin::IntA))
}
fn allocate_io_bars(

View file

@ -440,14 +440,15 @@ impl PciDevice for VirtioPciDevice {
fn assign_irq(
&mut self,
irq_evt: Event,
irq_resample_evt: Event,
irq_num: u32,
irq_pin: PciInterruptPin,
) {
self.config_regs.set_irq(irq_num as u8, irq_pin);
self.interrupt_evt = Some(irq_evt);
self.interrupt_resample_evt = Some(irq_resample_evt);
irq_evt: &Event,
irq_resample_evt: &Event,
irq_num: Option<u32>,
) -> Option<(u32, PciInterruptPin)> {
self.interrupt_evt = Some(irq_evt.try_clone().ok()?);
self.interrupt_resample_evt = Some(irq_resample_evt.try_clone().ok()?);
let gsi = irq_num?;
self.config_regs.set_irq(gsi as u8, PciInterruptPin::IntA);
Some((gsi, PciInterruptPin::IntA))
}
fn allocate_io_bars(