Allow per-cpu IRQ allocation.

This commit is contained in:
4lDO2
2020-04-22 13:43:45 +02:00
parent 4f62888bb4
commit cafee6ad7b
2 changed files with 42 additions and 19 deletions
+34 -13
View File
@@ -3,25 +3,45 @@
//! This module allows easy handling of the `irq:` scheme, and allocating interrupt vectors for use
//! by INTx#, MSI, or MSI-X.
use std::convert::TryFrom;
use std::fs::{self, File};
use std::io::{self, prelude::*};
use std::num::NonZeroU8;
use std::ops;
/// Read the local APIC ID of the bootstrap processor.
pub fn read_bsp_apic_id() -> io::Result<u32> {
pub fn read_bsp_apic_id() -> io::Result<usize> {
let mut buffer = [0u8; 8];
let mut file = File::open("irq:bsp")?;
let bytes_read = file.read(&mut buffer)?;
Ok(if bytes_read == 8 {
u64::from_le_bytes(buffer) as u32
(if bytes_read == 8 {
usize::try_from(u64::from_le_bytes(buffer))
} else if bytes_read == 4 {
u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]])
usize::try_from(u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]))
} else {
panic!("`irq:` scheme responded with {} bytes, expected {}", bytes_read, std::mem::size_of::<usize>());
})
}).or(Err(io::Error::new(io::ErrorKind::InvalidData, "bad BSP int size")))
}
// TODO: Perhaps read the MADT instead?
/// Obtains an interator over all of the visible CPU ids, for use in IRQ allocation and MSI
/// capability structs or MSI-X tables.
pub fn cpu_ids() -> io::Result<impl Iterator<Item = io::Result<usize>> + 'static> {
Ok(fs::read_dir("irq:")?
.filter_map(|entry| -> Option<io::Result<_>> { match entry {
Ok(e) => {
let path = e.path();
let file_name = path.file_name()?.to_str()?;
// the file name should be in the format `cpu-<CPU ID>`
if ! file_name.starts_with("cpu-") {
return None;
}
u8::from_str_radix(&file_name[4..], 16).map(usize::from).map(Ok).ok()
}
Err(e) => Some(Err(e)),
} }))
}
/// Allocate multiple interrupt vectors, from the IDT of the bootstrap processor, returning the
@@ -41,10 +61,13 @@ pub fn read_bsp_apic_id() -> io::Result<u32> {
/// individually allocated vectors that might be spread out, even on multiple CPUs. Thus, multiple
/// invocations with alignment 1 and count 1 are totally acceptable, although allocating in bulk
/// minimizes the initialization overhead, even though it's negligible.
pub fn allocate_aligned_interrupt_vectors(alignment: NonZeroU8, count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
///
/// Every interrupt vector will be allocated in the same CPU's IDT, as specified by `cpu_id`.
pub fn allocate_aligned_interrupt_vectors(cpu_id: usize, alignment: NonZeroU8, count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
let cpu_id = u8::try_from(cpu_id).expect("usize cpu ids not implemented yet");
if count == 0 { return Ok(None) }
let available_irqs = fs::read_dir("irq:")?;
let available_irqs = fs::read_dir(format!("irq:{:02x}", cpu_id))?;
let mut available_irq_numbers = available_irqs.filter_map(|entry| -> Option<io::Result<_>> {
let entry = match entry {
Ok(e) => e,
@@ -63,8 +86,6 @@ pub fn allocate_aligned_interrupt_vectors(alignment: NonZeroU8, count: u8) -> io
None => return None,
};
// note that there might be future subdirectories in the IRQ scheme, such as `cpu-<APIC
// ID>/<IRQ>`, thus no error but just None
match path_str.parse::<u8>() {
Ok(p) => Some(Ok(p)),
Err(_) => None,
@@ -119,15 +140,15 @@ pub fn allocate_aligned_interrupt_vectors(alignment: NonZeroU8, count: u8) -> io
/// Allocate at most `count` interrupt vectors, which can start at any offset. Unless MSI is used
/// and an entire aligned range of vectors is needed, this function should be used.
pub fn allocate_interrupt_vectors(count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
allocate_aligned_interrupt_vectors(NonZeroU8::new(1).unwrap(), count)
pub fn allocate_interrupt_vectors(cpu_id: usize, count: u8) -> io::Result<Option<(u8, Vec<File>)>> {
allocate_aligned_interrupt_vectors(cpu_id, NonZeroU8::new(1).unwrap(), count)
}
/// Allocate a single interrupt vector, returning both the vector number (starting from 32 up to
/// 254), and its IRQ handle which is then reserved. Returns Ok(None) if allocation fails due to
/// no available IRQs.
pub fn allocate_single_interrupt_vector() -> io::Result<Option<(u8, File)>> {
let (base, mut files) = match allocate_interrupt_vectors(1) {
pub fn allocate_single_interrupt_vector(cpu_id: usize) -> io::Result<Option<(u8, File)>> {
let (base, mut files) = match allocate_interrupt_vectors(cpu_id, 1) {
Ok(Some((base, files))) => (base, files),
Ok(None) => return Ok(None),
Err(err) => return Err(err),