Files
cloud-hypervisor/vmm/src/interrupt.rs
Jinank Jain ea4693a091 misc: Fix clippy error from beta compiler
Rust has a new way of constructing other error and clippy complains if
we are still using the older way to construct error message. Thus,
migrate to the new approach suggested by the clippy.

Warning from beta compiler:

error: this can be `std::io::Error::other(_)`
--> block/src/vhdx/mod.rs:142:17
 |
 | /                 std::io::Error::new(
 | |                     std::io::ErrorKind::Other,
 | |                     format!("Failed to update VHDx header: {e}"),
 | |                 )
 | |_________________^
 |
 = help: for further information visit
https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error
help: use `std::io::Error::other`

                 std::io::Error::other(
                     format!("Failed to update VHDx header: {e}"),

Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
2025-04-03 13:11:49 +00:00

313 lines
8.8 KiB
Rust

// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
//
use std::collections::HashMap;
use std::io;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use devices::interrupt_controller::InterruptController;
use hypervisor::IrqRoutingEntry;
use vm_allocator::SystemAllocator;
use vm_device::interrupt::{
InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
LegacyIrqGroupConfig, MsiIrqGroupConfig,
};
use vmm_sys_util::eventfd::EventFd;
/// Reuse std::io::Result to simplify interoperability among crates.
pub type Result<T> = std::io::Result<T>;
struct InterruptRoute {
gsi: u32,
irq_fd: EventFd,
registered: AtomicBool,
}
impl InterruptRoute {
pub fn new(allocator: &mut SystemAllocator) -> Result<Self> {
let irq_fd = EventFd::new(libc::EFD_NONBLOCK)?;
let gsi = allocator
.allocate_gsi()
.ok_or_else(|| io::Error::other("Failed allocating new GSI"))?;
Ok(InterruptRoute {
gsi,
irq_fd,
registered: AtomicBool::new(false),
})
}
pub fn enable(&self, vm: &Arc<dyn hypervisor::Vm>) -> Result<()> {
if !self.registered.load(Ordering::Acquire) {
vm.register_irqfd(&self.irq_fd, self.gsi)
.map_err(|e| io::Error::other(format!("Failed registering irq_fd: {e}")))?;
// Update internals to track the irq_fd as "registered".
self.registered.store(true, Ordering::Release);
}
Ok(())
}
pub fn disable(&self, vm: &Arc<dyn hypervisor::Vm>) -> Result<()> {
if self.registered.load(Ordering::Acquire) {
vm.unregister_irqfd(&self.irq_fd, self.gsi)
.map_err(|e| io::Error::other(format!("Failed unregistering irq_fd: {e}")))?;
// Update internals to track the irq_fd as "unregistered".
self.registered.store(false, Ordering::Release);
}
Ok(())
}
pub fn trigger(&self) -> Result<()> {
self.irq_fd.write(1)
}
pub fn notifier(&self) -> Option<EventFd> {
Some(
self.irq_fd
.try_clone()
.expect("Failed cloning interrupt's EventFd"),
)
}
}
pub struct RoutingEntry {
route: IrqRoutingEntry,
masked: bool,
}
pub struct MsiInterruptGroup {
vm: Arc<dyn hypervisor::Vm>,
gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry>>>,
irq_routes: HashMap<InterruptIndex, InterruptRoute>,
}
impl MsiInterruptGroup {
fn set_gsi_routes(&self, routes: &HashMap<u32, RoutingEntry>) -> Result<()> {
let mut entry_vec: Vec<IrqRoutingEntry> = Vec::new();
for (_, entry) in routes.iter() {
if entry.masked {
continue;
}
entry_vec.push(entry.route);
}
self.vm
.set_gsi_routing(&entry_vec)
.map_err(|e| io::Error::other(format!("Failed setting GSI routing: {e}")))
}
}
impl MsiInterruptGroup {
fn new(
vm: Arc<dyn hypervisor::Vm>,
gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry>>>,
irq_routes: HashMap<InterruptIndex, InterruptRoute>,
) -> Self {
MsiInterruptGroup {
vm,
gsi_msi_routes,
irq_routes,
}
}
}
impl InterruptSourceGroup for MsiInterruptGroup {
fn enable(&self) -> Result<()> {
for (_, route) in self.irq_routes.iter() {
route.enable(&self.vm)?;
}
Ok(())
}
fn disable(&self) -> Result<()> {
for (_, route) in self.irq_routes.iter() {
route.disable(&self.vm)?;
}
Ok(())
}
fn trigger(&self, index: InterruptIndex) -> Result<()> {
if let Some(route) = self.irq_routes.get(&index) {
return route.trigger();
}
Err(io::Error::other(format!(
"trigger: Invalid interrupt index {index}"
)))
}
fn notifier(&self, index: InterruptIndex) -> Option<EventFd> {
if let Some(route) = self.irq_routes.get(&index) {
return route.notifier();
}
None
}
fn update(
&self,
index: InterruptIndex,
config: InterruptSourceConfig,
masked: bool,
set_gsi: bool,
) -> Result<()> {
if let Some(route) = self.irq_routes.get(&index) {
let entry = RoutingEntry {
route: self.vm.make_routing_entry(route.gsi, &config),
masked,
};
// When mask a msi irq, entry.masked is set to be true,
// and the gsi will not be passed to KVM through KVM_SET_GSI_ROUTING.
// So it's required to call disable() (which deassign KVM_IRQFD) before
// set_gsi_routes() to avoid kernel panic (see #3827)
if masked {
route.disable(&self.vm)?;
}
let mut routes = self.gsi_msi_routes.lock().unwrap();
routes.insert(route.gsi, entry);
if set_gsi {
self.set_gsi_routes(&routes)?;
}
// Assign KVM_IRQFD after KVM_SET_GSI_ROUTING to avoid
// panic on kernel which not have commit a80ced6ea514
// (KVM: SVM: fix panic on out-of-bounds guest IRQ).
if !masked {
route.enable(&self.vm)?;
}
return Ok(());
}
Err(io::Error::other(format!(
"update: Invalid interrupt index {index}"
)))
}
fn set_gsi(&self) -> Result<()> {
let routes = self.gsi_msi_routes.lock().unwrap();
self.set_gsi_routes(&routes)
}
}
pub struct LegacyUserspaceInterruptGroup {
ioapic: Arc<Mutex<dyn InterruptController>>,
irq: u32,
}
impl LegacyUserspaceInterruptGroup {
fn new(ioapic: Arc<Mutex<dyn InterruptController>>, irq: u32) -> Self {
LegacyUserspaceInterruptGroup { ioapic, irq }
}
}
impl InterruptSourceGroup for LegacyUserspaceInterruptGroup {
fn trigger(&self, _index: InterruptIndex) -> Result<()> {
self.ioapic
.lock()
.unwrap()
.service_irq(self.irq as usize)
.map_err(|e| io::Error::other(format!("failed to inject IRQ #{}: {:?}", self.irq, e)))
}
fn update(
&self,
_index: InterruptIndex,
_config: InterruptSourceConfig,
_masked: bool,
_set_gsi: bool,
) -> Result<()> {
Ok(())
}
fn set_gsi(&self) -> Result<()> {
Ok(())
}
fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
self.ioapic.lock().unwrap().notifier(self.irq as usize)
}
}
pub struct LegacyUserspaceInterruptManager {
ioapic: Arc<Mutex<dyn InterruptController>>,
}
pub struct MsiInterruptManager {
allocator: Arc<Mutex<SystemAllocator>>,
vm: Arc<dyn hypervisor::Vm>,
gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry>>>,
}
impl LegacyUserspaceInterruptManager {
pub fn new(ioapic: Arc<Mutex<dyn InterruptController>>) -> Self {
LegacyUserspaceInterruptManager { ioapic }
}
}
impl MsiInterruptManager {
pub fn new(allocator: Arc<Mutex<SystemAllocator>>, vm: Arc<dyn hypervisor::Vm>) -> Self {
// Create a shared list of GSI that can be shared through all PCI
// devices. This way, we can maintain the full list of used GSI,
// preventing one device from overriding interrupts setting from
// another one.
let gsi_msi_routes = Arc::new(Mutex::new(HashMap::new()));
MsiInterruptManager {
allocator,
vm,
gsi_msi_routes,
}
}
}
impl InterruptManager for LegacyUserspaceInterruptManager {
type GroupConfig = LegacyIrqGroupConfig;
fn create_group(&self, config: Self::GroupConfig) -> Result<Arc<dyn InterruptSourceGroup>> {
Ok(Arc::new(LegacyUserspaceInterruptGroup::new(
self.ioapic.clone(),
config.irq,
)))
}
fn destroy_group(&self, _group: Arc<dyn InterruptSourceGroup>) -> Result<()> {
Ok(())
}
}
impl InterruptManager for MsiInterruptManager {
type GroupConfig = MsiIrqGroupConfig;
fn create_group(&self, config: Self::GroupConfig) -> Result<Arc<dyn InterruptSourceGroup>> {
let mut allocator = self.allocator.lock().unwrap();
let mut irq_routes: HashMap<InterruptIndex, InterruptRoute> =
HashMap::with_capacity(config.count as usize);
for i in config.base..config.base + config.count {
irq_routes.insert(i, InterruptRoute::new(&mut allocator)?);
}
Ok(Arc::new(MsiInterruptGroup::new(
self.vm.clone(),
self.gsi_msi_routes.clone(),
irq_routes,
)))
}
fn destroy_group(&self, _group: Arc<dyn InterruptSourceGroup>) -> Result<()> {
Ok(())
}
}