vm-allocator: Expand the IRQ allocation API to support GSI

GSI (Global System Interrupt) is an extension of just a linear array of
IRQs. It takes IOAPICs into account for example.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Samuel Ortiz 2019-07-11 11:48:14 +02:00 committed by Sebastien Boeuf
parent 96fb38a5aa
commit 0a04a950a1
4 changed files with 105 additions and 13 deletions

84
vm-allocator/src/gsi.rs Normal file
View File

@ -0,0 +1,84 @@
// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
use std::collections::btree_map::BTreeMap;
use std::result;
#[derive(Debug)]
pub enum Error {
Overflow,
}
pub type Result<T> = result::Result<T, Error>;
/// GsiApic
#[derive(Copy, Clone)]
pub struct GsiApic {
base: u32,
irqs: u32,
}
impl GsiApic {
/// New GSI APIC
pub fn new(base: u32, irqs: u32) -> Self {
GsiApic { base, irqs }
}
}
/// GsiAllocator
pub struct GsiAllocator {
apics: BTreeMap<u32, u32>,
next_irq: u32,
next_gsi: u32,
}
impl GsiAllocator {
/// New GSI allocator
pub fn new(apics: Vec<GsiApic>) -> Self {
let mut allocator = GsiAllocator {
apics: BTreeMap::new(),
next_irq: 0xffff_ffff,
next_gsi: 0,
};
for apic in &apics {
if apic.base < allocator.next_irq {
allocator.next_irq = apic.base;
}
if apic.base + apic.irqs > allocator.next_gsi {
allocator.next_gsi = apic.base + apic.irqs;
}
allocator.apics.insert(apic.base, apic.irqs);
}
allocator
}
/// Allocate a GSI
pub fn allocate_gsi(&mut self) -> Result<u32> {
self.next_gsi = self.next_gsi.checked_add(1).ok_or(Error::Overflow)?;
Ok(self.next_gsi - 1)
}
/// Allocate an IRQ
pub fn allocate_irq(&mut self) -> Result<u32> {
let mut irq: u32 = 0;
for (base, irqs) in self.apics.iter() {
// HACKHACK - This only works with 1 single IOAPIC...
if self.next_irq >= *base && self.next_irq < *base + *irqs {
irq = self.next_irq;
self.next_irq += 1;
}
}
if irq == 0 {
return Err(Error::Overflow);
}
Ok(irq)
}
}

View File

@ -14,7 +14,9 @@ extern crate libc;
extern crate vm_memory;
mod address;
mod gsi;
mod system;
pub use crate::address::AddressAllocator;
pub use crate::gsi::{GsiAllocator, GsiApic};
pub use crate::system::SystemAllocator;

View File

@ -10,6 +10,7 @@
use vm_memory::{GuestAddress, GuestUsize};
use crate::address::AddressAllocator;
use crate::gsi::{GsiAllocator, GsiApic};
use libc::{sysconf, _SC_PAGESIZE};
@ -25,12 +26,12 @@ fn pagesize() -> usize {
/// # Example - Use the `SystemAddress` builder.
///
/// ```
/// # use vm_allocator::SystemAllocator;
/// # use vm_allocator::{GsiApic, SystemAllocator};
/// # use vm_memory::{Address, GuestAddress, GuestUsize};
/// let mut allocator = SystemAllocator::new(
/// GuestAddress(0x1000), 0x10000,
/// GuestAddress(0x10000000), 0x10000000,
/// 5).unwrap();
/// vec![GsiApic::new(5, 19)]).unwrap();
/// assert_eq!(allocator.allocate_irq(), Some(5));
/// assert_eq!(allocator.allocate_irq(), Some(6));
/// assert_eq!(allocator.allocate_mmio_addresses(None, 0x1000, Some(0x1000)), Some(GuestAddress(0x1fffe000)));
@ -39,7 +40,7 @@ fn pagesize() -> usize {
pub struct SystemAllocator {
io_address_space: AddressAllocator,
mmio_address_space: AddressAllocator,
next_irq: u32,
gsi_allocator: GsiAllocator,
}
impl SystemAllocator {
@ -56,23 +57,23 @@ impl SystemAllocator {
io_size: GuestUsize,
mmio_base: GuestAddress,
mmio_size: GuestUsize,
first_irq: u32,
apics: Vec<GsiApic>,
) -> Option<Self> {
Some(SystemAllocator {
io_address_space: AddressAllocator::new(io_base, io_size)?,
mmio_address_space: AddressAllocator::new(mmio_base, mmio_size)?,
next_irq: first_irq,
gsi_allocator: GsiAllocator::new(apics),
})
}
/// Reserves the next available system irq number.
pub fn allocate_irq(&mut self) -> Option<u32> {
if let Some(irq_num) = self.next_irq.checked_add(1) {
self.next_irq = irq_num;
Some(irq_num - 1)
} else {
None
}
self.gsi_allocator.allocate_irq().ok()
}
/// Reserves the next available GSI.
pub fn allocate_gsi(&mut self) -> Option<u32> {
self.gsi_allocator.allocate_gsi().ok()
}
/// Reserves a section of `size` bytes of IO address space.

View File

@ -44,7 +44,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
use std::ptr::null_mut;
use std::sync::{Arc, Barrier, Mutex};
use std::{result, str, thread};
use vm_allocator::SystemAllocator;
use vm_allocator::{GsiApic, SystemAllocator};
use vm_memory::guest_memory::FileOffset;
use vm_memory::{
Address, Bytes, Error as MmapError, GuestAddress, GuestMemory, GuestMemoryMmap,
@ -1066,13 +1066,18 @@ impl<'a> Vm<'a> {
CpuidPatch::patch_cpuid(&mut cpuid, cpuid_patches);
let ioapic = GsiApic::new(
X86_64_IRQ_BASE,
ioapic::NUM_IOAPIC_PINS as u32 - X86_64_IRQ_BASE,
);
// Let's allocate 64 GiB of addressable MMIO space, starting at 0.
let mut allocator = SystemAllocator::new(
GuestAddress(0),
1 << 16 as GuestUsize,
GuestAddress(0),
1 << 36 as GuestUsize,
X86_64_IRQ_BASE,
vec![ioapic],
)
.ok_or(Error::CreateSystemAllocator)?;