ioapic: Rely fully on the InterruptSourceGroup to manage interrupts

This commit relies on the interrupt manager and the resulting interrupt
source group to abstract the knowledge about KVM and how interrupts are
updated and delivered.

This allows the entire "devices" crate to be freed from kvm_ioctls and
kvm_bindings dependencies.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2020-01-22 23:45:27 +01:00 committed by Rob Bradford
parent 2dca959084
commit 0042f1de75
5 changed files with 69 additions and 54 deletions

2
Cargo.lock generated
View File

@ -226,8 +226,6 @@ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"epoll 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kvm-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kvm-ioctls 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -7,8 +7,6 @@ authors = ["The Chromium OS Authors"]
bitflags = ">=1.2.1"
byteorder = "1.3.2"
epoll = ">=4.0.1"
kvm-bindings = "0.2.0"
kvm-ioctls = "0.4.0"
libc = "0.2.60"
log = "0.4.8"
vm-device = { path = "../vm-device" }

View File

@ -11,18 +11,17 @@
use crate::BusDevice;
use byteorder::{ByteOrder, LittleEndian};
use kvm_bindings::kvm_msi;
use kvm_ioctls::VmFd;
use std::io;
use std::result;
use std::sync::Arc;
use vm_device::interrupt::{InterruptIndex, InterruptManager, InterruptSourceGroup, PCI_MSI_IRQ};
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
MsiIrqSourceConfig, PCI_MSI_IRQ,
};
use vm_memory::GuestAddress;
#[derive(Debug)]
pub enum Error {
/// Failed to send an interrupt.
InterruptFailed(kvm_ioctls::Error),
/// Invalid destination mode.
InvalidDestinationMode,
/// Invalid trigger mode.
@ -31,6 +30,16 @@ pub enum Error {
InvalidDeliveryMode,
/// Failed creating the interrupt source group.
CreateInterruptSourceGroup(io::Error),
/// Failed triggering the interrupt.
TriggerInterrupt(io::Error),
/// Failed masking the interrupt.
MaskInterrupt(io::Error),
/// Failed unmasking the interrupt.
UnmaskInterrupt(io::Error),
/// Failed updating the interrupt.
UpdateInterrupt(io::Error),
/// Failed enabling the interrupt.
EnableInterrupt(io::Error),
}
type Result<T> = result::Result<T, Error>;
@ -160,9 +169,8 @@ pub struct Ioapic {
id: u32,
reg_sel: u32,
reg_entries: [RedirectionTableEntry; NUM_IOAPIC_PINS],
vm_fd: Arc<VmFd>,
apic_address: GuestAddress,
_interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
interrupt_source_group: Arc<Box<dyn InterruptSourceGroup>>,
}
impl BusDevice for Ioapic {
@ -202,7 +210,6 @@ impl BusDevice for Ioapic {
impl Ioapic {
pub fn new(
vm_fd: Arc<VmFd>,
apic_address: GuestAddress,
interrupt_manager: Arc<dyn InterruptManager>,
) -> Result<Ioapic> {
@ -214,13 +221,16 @@ impl Ioapic {
)
.map_err(Error::CreateInterruptSourceGroup)?;
interrupt_source_group
.enable()
.map_err(Error::EnableInterrupt)?;
Ok(Ioapic {
id: 0,
reg_sel: 0,
reg_entries: [0; NUM_IOAPIC_PINS],
vm_fd,
apic_address,
_interrupt_source_group: interrupt_source_group,
interrupt_source_group,
})
}
@ -241,16 +251,30 @@ impl Ioapic {
pub fn service_irq(&mut self, irq: usize) -> Result<()> {
let entry = &mut self.reg_entries[irq];
// Don't inject the interrupt if the IRQ is masked
if interrupt_mask(*entry) == 1 {
return Ok(());
self.interrupt_source_group
.trigger(irq as InterruptIndex)
.map_err(Error::TriggerInterrupt)?;
debug!("Interrupt successfully delivered");
// If trigger mode is level sensitive, set the Remote IRR bit.
// It will be cleared when the EOI is received.
if trigger_mode(*entry) == 1 {
set_remote_irr(entry, 1);
}
// Clear the Delivery Status bit
set_delivery_status(entry, 0);
Ok(())
}
fn update_entry(&self, irq: usize) -> Result<()> {
let entry = self.reg_entries[irq];
// Validate Destination Mode value, and retrieve Destination ID
let destination_mode = destination_mode(*entry);
let destination_mode = destination_mode(entry);
let destination_id: u8 = match destination_mode {
x if x == DestinationMode::Physical as u8 => destination_field_physical(*entry),
x if x == DestinationMode::Logical as u8 => destination_field_logical(*entry),
x if x == DestinationMode::Physical as u8 => destination_field_physical(entry),
x if x == DestinationMode::Logical as u8 => destination_field_logical(entry),
_ => return Err(Error::InvalidDestinationMode),
};
@ -260,20 +284,20 @@ impl Ioapic {
let redirection_hint: u8 = 1;
// Generate MSI message address
let address_lo: u32 = self.apic_address.0 as u32
let low_addr: u32 = self.apic_address.0 as u32
| u32::from(destination_id) << 12
| u32::from(redirection_hint) << 3
| u32::from(destination_mode) << 2;
// Validate Trigger Mode value
let trigger_mode = trigger_mode(*entry);
let trigger_mode = trigger_mode(entry);
match trigger_mode {
x if (x == TriggerMode::Edge as u8) || (x == TriggerMode::Level as u8) => {}
_ => return Err(Error::InvalidTriggerMode),
}
// Validate Delivery Mode value
let delivery_mode = delivery_mode(*entry);
let delivery_mode = delivery_mode(entry);
match delivery_mode {
x if (x == DeliveryMode::Fixed as u8)
|| (x == DeliveryMode::Lowest as u8)
@ -288,37 +312,31 @@ impl Ioapic {
// Generate MSI message data
let data: u32 = u32::from(trigger_mode) << 15
| u32::from(remote_irr(*entry)) << 14
| u32::from(remote_irr(entry)) << 14
| u32::from(delivery_mode) << 8
| u32::from(vector(*entry));
| u32::from(vector(entry));
let msi = kvm_msi {
address_lo,
address_hi: 0x0,
let config = MsiIrqSourceConfig {
high_addr: 0x0,
low_addr,
data,
flags: 0u32,
devid: 0u32,
pad: [0u8; 12],
};
match self.vm_fd.signal_msi(msi) {
Ok(ret) => {
if ret > 0 {
debug!("MSI message successfully delivered");
// If trigger mode is level sensitive, set the Remote IRR bit.
// It will be cleared when the EOI is received.
if trigger_mode == 1 {
set_remote_irr(entry, 1);
}
// Clear the Delivery Status bit
set_delivery_status(entry, 0);
} else {
warn!("failed to deliver MSI message, blocked by guest");
}
Ok(())
}
Err(e) => Err(Error::InterruptFailed(e)),
self.interrupt_source_group
.update(irq as InterruptIndex, InterruptSourceConfig::MsiIrq(config))
.map_err(Error::UpdateInterrupt)?;
if interrupt_mask(entry) == 1 {
self.interrupt_source_group
.mask(irq as InterruptIndex)
.map_err(Error::MaskInterrupt)?;
} else {
self.interrupt_source_group
.unmask(irq as InterruptIndex)
.map_err(Error::UnmaskInterrupt)?;
}
Ok(())
}
fn ioapic_write(&mut self, val: u32) {
@ -338,6 +356,11 @@ impl Ioapic {
self.reg_entries[index] &= 0xffff_ffff_0000_5000;
self.reg_entries[index] |= u64::from(val) & 0xffff_afff;
}
// The entry must be updated through the interrupt source
// group.
if let Err(e) = self.update_entry(index) {
error!("Failed updating IOAPIC entry: {:?}", e);
}
}
_ => error!("IOAPIC: invalid write to register offset"),
}

View File

@ -10,8 +10,6 @@
extern crate bitflags;
extern crate byteorder;
extern crate epoll;
extern crate kvm_bindings;
extern crate kvm_ioctls;
extern crate libc;
#[macro_use]
extern crate log;

View File

@ -451,8 +451,7 @@ impl DeviceManager {
None,
));
let ioapic =
DeviceManager::add_ioapic(vm_info, &address_manager, ioapic_interrupt_manager)?;
let ioapic = DeviceManager::add_ioapic(&address_manager, ioapic_interrupt_manager)?;
// Creation of the global interrupt manager, which can take a hold onto
// the brand new Ioapic.
@ -698,13 +697,12 @@ impl DeviceManager {
}
fn add_ioapic(
vm_info: &VmInfo,
address_manager: &Arc<AddressManager>,
interrupt_manager: Arc<dyn InterruptManager>,
) -> DeviceManagerResult<Arc<Mutex<ioapic::Ioapic>>> {
// Create IOAPIC
let ioapic = Arc::new(Mutex::new(
ioapic::Ioapic::new(vm_info.vm_fd.clone(), APIC_START, interrupt_manager)
ioapic::Ioapic::new(APIC_START, interrupt_manager)
.map_err(DeviceManagerError::CreateIoapic)?,
));