From 92082ad439ad444a94b6a3f044779557f9f3b805 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Tue, 14 Jan 2020 15:31:36 +0100 Subject: [PATCH] vmm: Fully implement interrupt traits After the skeleton of InterruptManager and InterruptSourceGroup traits have been implemented, this new commit takes care of fully implementing the content of KvmInterruptManager (InterruptManager trait) and MsiInterruptGroup (InterruptSourceGroup). Signed-off-by: Sebastien Boeuf --- vmm/src/interrupt.rs | 275 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 263 insertions(+), 12 deletions(-) diff --git a/vmm/src/interrupt.rs b/vmm/src/interrupt.rs index e48c60a20..45c34f349 100644 --- a/vmm/src/interrupt.rs +++ b/vmm/src/interrupt.rs @@ -3,43 +3,294 @@ // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause // -use std::sync::Arc; +use kvm_bindings::{kvm_irq_routing, kvm_irq_routing_entry, KVM_IRQ_ROUTING_MSI}; +use kvm_ioctls::VmFd; +use std::collections::HashMap; +use std::io; +use std::mem::size_of; +use std::sync::{Arc, Mutex}; +use vm_allocator::SystemAllocator; use vm_device::interrupt::{ InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup, InterruptType, + PCI_MSI_IRQ, }; +use vmm_sys_util::eventfd::EventFd; /// Reuse std::io::Result to simplify interoperability among crates. pub type Result = std::io::Result; -pub struct MsiInterruptGroup {} +// Returns a `Vec` with a size in bytes at least as large as `size_in_bytes`. +fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { + let rounded_size = (size_in_bytes + size_of::() - 1) / size_of::(); + let mut v = Vec::with_capacity(rounded_size); + v.resize_with(rounded_size, T::default); + v +} + +// The kvm API has many structs that resemble the following `Foo` structure: +// +// ``` +// #[repr(C)] +// struct Foo { +// some_data: u32 +// entries: __IncompleteArrayField<__u32>, +// } +// ``` +// +// In order to allocate such a structure, `size_of::()` would be too small because it would not +// include any space for `entries`. To make the allocation large enough while still being aligned +// for `Foo`, a `Vec` is created. Only the first element of `Vec` would actually be used +// as a `Foo`. The remaining memory in the `Vec` is for `entries`, which must be contiguous +// with `Foo`. This function is used to make the `Vec` with enough space for `count` entries. +pub fn vec_with_array_field(count: usize) -> Vec { + let element_space = count * size_of::(); + let vec_size_bytes = size_of::() + element_space; + vec_with_size_in_bytes(vec_size_bytes) +} + +pub struct InterruptRoute { + pub gsi: u32, + pub irq_fd: EventFd, +} + +impl InterruptRoute { + pub fn new(allocator: &mut SystemAllocator) -> Result { + let irq_fd = EventFd::new(libc::EFD_NONBLOCK)?; + let gsi = allocator + .allocate_gsi() + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed allocating new GSI"))?; + + Ok(InterruptRoute { gsi, irq_fd }) + } + + pub fn enable(&self, vm: &Arc) -> Result<()> { + vm.register_irqfd(&self.irq_fd, self.gsi).map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("Failed registering irq_fd: {}", e), + ) + }) + } + + pub fn disable(&self, vm: &Arc) -> Result<()> { + vm.unregister_irqfd(&self.irq_fd, self.gsi).map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("Failed unregistering irq_fd: {}", e), + ) + }) + } +} + +pub struct KvmRoutingEntry { + kvm_route: kvm_irq_routing_entry, + masked: bool, +} + +pub struct MsiInterruptGroup { + vm_fd: Arc, + gsi_msi_routes: Arc>>, + irq_routes: HashMap, +} impl MsiInterruptGroup { - fn new() -> Self { - MsiInterruptGroup {} + fn new( + vm_fd: Arc, + gsi_msi_routes: Arc>>, + irq_routes: HashMap, + ) -> Self { + MsiInterruptGroup { + vm_fd, + gsi_msi_routes, + irq_routes, + } + } + + fn set_kvm_gsi_routes(&self) -> Result<()> { + let gsi_msi_routes = self.gsi_msi_routes.lock().unwrap(); + let mut entry_vec: Vec = Vec::new(); + for (_, entry) in gsi_msi_routes.iter() { + if entry.masked { + continue; + } + + entry_vec.push(entry.kvm_route); + } + + let mut irq_routing = + vec_with_array_field::(entry_vec.len()); + irq_routing[0].nr = entry_vec.len() as u32; + irq_routing[0].flags = 0; + + unsafe { + let entries: &mut [kvm_irq_routing_entry] = + irq_routing[0].entries.as_mut_slice(entry_vec.len()); + entries.copy_from_slice(&entry_vec); + } + + self.vm_fd.set_gsi_routing(&irq_routing[0]).map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("Failed setting GSI routing: {}", e), + ) + }) + } + + fn mask_kvm_entry(&self, index: InterruptIndex, mask: bool) -> Result<()> { + if let Some(route) = self.irq_routes.get(&index) { + let mut gsi_msi_routes = self.gsi_msi_routes.lock().unwrap(); + if let Some(kvm_entry) = gsi_msi_routes.get_mut(&route.gsi) { + kvm_entry.masked = mask; + } else { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("mask: No existing route for interrupt index {}", index), + )); + } + } else { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("mask: Invalid interrupt index {}", index), + )); + } + + self.set_kvm_gsi_routes() } } impl InterruptSourceGroup for MsiInterruptGroup { - fn trigger(&self, _index: InterruptIndex) -> Result<()> { + fn enable(&self) -> Result<()> { + for (_, route) in self.irq_routes.iter() { + route.enable(&self.vm_fd)?; + } + Ok(()) } - fn update(&self, _index: InterruptIndex, _config: InterruptSourceConfig) -> Result<()> { + fn disable(&self) -> Result<()> { + for (_, route) in self.irq_routes.iter() { + route.disable(&self.vm_fd)?; + } + Ok(()) } + + fn trigger(&self, index: InterruptIndex) -> Result<()> { + if let Some(route) = self.irq_routes.get(&index) { + return route.irq_fd.write(1); + } + + Err(io::Error::new( + io::ErrorKind::Other, + format!("trigger: Invalid interrupt index {}", index), + )) + } + + fn notifier(&self, index: InterruptIndex) -> Option<&EventFd> { + if let Some(route) = self.irq_routes.get(&index) { + return Some(&route.irq_fd); + } + + None + } + + fn update(&self, index: InterruptIndex, config: InterruptSourceConfig) -> Result<()> { + if let Some(route) = self.irq_routes.get(&index) { + if let InterruptSourceConfig::MsiIrq(cfg) = &config { + let mut kvm_route = kvm_irq_routing_entry { + gsi: route.gsi, + type_: KVM_IRQ_ROUTING_MSI, + ..Default::default() + }; + + kvm_route.u.msi.address_lo = cfg.low_addr; + kvm_route.u.msi.address_hi = cfg.high_addr; + kvm_route.u.msi.data = cfg.data; + + let kvm_entry = KvmRoutingEntry { + kvm_route, + masked: false, + }; + + self.gsi_msi_routes + .lock() + .unwrap() + .insert(route.gsi, kvm_entry); + } else { + return Err(io::Error::new( + io::ErrorKind::Other, + "Interrupt config type not supported", + )); + } + + return self.set_kvm_gsi_routes(); + } + + Err(io::Error::new( + io::ErrorKind::Other, + format!("update: Invalid interrupt index {}", index), + )) + } + + fn mask(&self, index: InterruptIndex) -> Result<()> { + self.mask_kvm_entry(index, true) + } + + fn unmask(&self, index: InterruptIndex) -> Result<()> { + self.mask_kvm_entry(index, false) + } } -pub struct KvmInterruptManager {} +pub struct KvmInterruptManager { + allocator: Arc>, + vm_fd: Arc, + gsi_msi_routes: Arc>>, +} + +impl KvmInterruptManager { + pub fn new( + allocator: Arc>, + vm_fd: Arc, + gsi_msi_routes: Arc>>, + ) -> Self { + KvmInterruptManager { + allocator, + vm_fd, + gsi_msi_routes, + } + } +} impl InterruptManager for KvmInterruptManager { fn create_group( &self, - _interrupt_type: InterruptType, - _base: InterruptIndex, - _count: InterruptIndex, + interrupt_type: InterruptType, + base: InterruptIndex, + count: InterruptIndex, ) -> Result>> { - let interrupt_source_group = MsiInterruptGroup::new(); - Ok(Arc::new(Box::new(interrupt_source_group))) + let mut allocator = self.allocator.lock().unwrap(); + + let mut irq_routes: HashMap = + HashMap::with_capacity(count as usize); + for i in base..count { + irq_routes.insert(i, InterruptRoute::new(&mut allocator)?); + } + + let interrupt_source_group: Arc> = match interrupt_type { + PCI_MSI_IRQ => Arc::new(Box::new(MsiInterruptGroup::new( + self.vm_fd.clone(), + self.gsi_msi_routes.clone(), + irq_routes, + ))), + _ => { + return Err(io::Error::new( + io::ErrorKind::Other, + "Interrupt type not supported", + )) + } + }; + + Ok(interrupt_source_group) } fn destroy_group(&self, _group: Arc>) -> Result<()> {