Skip to content

Commit 71ca309

Browse files
committed
virtio: add generic interrupt trait
Describing the APIs that need to implement types that are used as interrupts for VirtIO devices. Currently, we only use `IrqInterrupt` interrupts, but this will change once we have MSI-X with PCIe devices. Signed-off-by: Babis Chalios <bchalios@amazon.es>
1 parent f73206d commit 71ca309

File tree

2 files changed

+88
-27
lines changed

2 files changed

+88
-27
lines changed

src/vmm/src/devices/virtio/transport/mmio.rs

+56-27
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::sync::{Arc, Mutex, MutexGuard};
1111

1212
use vmm_sys_util::eventfd::EventFd;
1313

14+
use super::{VirtioInterrupt, VirtioInterruptType};
1415
use crate::devices::virtio::device::VirtioDevice;
1516
use crate::devices::virtio::device_status;
1617
use crate::devices::virtio::queue::Queue;
@@ -375,6 +376,15 @@ pub enum IrqType {
375376
Vring,
376377
}
377378

379+
impl From<VirtioInterruptType> for IrqType {
380+
fn from(interrupt_type: VirtioInterruptType) -> Self {
381+
match interrupt_type {
382+
VirtioInterruptType::Config => IrqType::Config,
383+
VirtioInterruptType::Queue(_) => IrqType::Vring,
384+
}
385+
}
386+
}
387+
378388
/// Helper struct that is responsible for triggering guest IRQs
379389
#[derive(Debug)]
380390
pub struct IrqTrigger {
@@ -388,6 +398,40 @@ impl Default for IrqTrigger {
388398
}
389399
}
390400

401+
impl VirtioInterrupt for IrqTrigger {
402+
fn trigger(&self, interrupt_type: VirtioInterruptType) -> Result<(), std::io::Error> {
403+
match interrupt_type {
404+
VirtioInterruptType::Config => self.trigger_irq(IrqType::Config),
405+
VirtioInterruptType::Queue(_) => self.trigger_irq(IrqType::Vring),
406+
}
407+
}
408+
409+
fn notifier(&self, _interrupt_type: VirtioInterruptType) -> Option<&EventFd> {
410+
Some(&self.irq_evt)
411+
}
412+
413+
fn status(&self) -> Arc<AtomicU32> {
414+
self.irq_status.clone()
415+
}
416+
417+
#[cfg(test)]
418+
fn has_pending_interrupt(&self, interrupt_type: VirtioInterruptType) -> bool {
419+
if let Ok(num_irqs) = self.irq_evt.read() {
420+
if num_irqs == 0 {
421+
return false;
422+
}
423+
424+
let irq_status = self.irq_status.load(Ordering::SeqCst);
425+
return matches!(
426+
(irq_status, interrupt_type.into()),
427+
(VIRTIO_MMIO_INT_CONFIG, IrqType::Config) | (VIRTIO_MMIO_INT_VRING, IrqType::Vring)
428+
);
429+
}
430+
431+
false
432+
}
433+
}
434+
391435
impl IrqTrigger {
392436
pub fn new() -> Self {
393437
Self {
@@ -1060,44 +1104,29 @@ pub(crate) mod tests {
10601104
assert_eq!(dummy_dev.acked_features(), 24);
10611105
}
10621106

1063-
impl IrqTrigger {
1064-
pub fn has_pending_irq(&self, irq_type: IrqType) -> bool {
1065-
if let Ok(num_irqs) = self.irq_evt.read() {
1066-
if num_irqs == 0 {
1067-
return false;
1068-
}
1069-
1070-
let irq_status = self.irq_status.load(Ordering::SeqCst);
1071-
return matches!(
1072-
(irq_status, irq_type),
1073-
(VIRTIO_MMIO_INT_CONFIG, IrqType::Config)
1074-
| (VIRTIO_MMIO_INT_VRING, IrqType::Vring)
1075-
);
1076-
}
1077-
1078-
false
1079-
}
1080-
}
1081-
10821107
#[test]
10831108
fn irq_trigger() {
10841109
let irq_trigger = IrqTrigger::new();
10851110
assert_eq!(irq_trigger.irq_status.load(Ordering::SeqCst), 0);
10861111

10871112
// Check that there are no pending irqs.
1088-
assert!(!irq_trigger.has_pending_irq(IrqType::Config));
1089-
assert!(!irq_trigger.has_pending_irq(IrqType::Vring));
1113+
assert!(!irq_trigger.has_pending_interrupt(VirtioInterruptType::Config));
1114+
assert!(!irq_trigger.has_pending_interrupt(VirtioInterruptType::Queue(0)));
10901115

10911116
// Check that trigger_irq() correctly generates irqs.
1092-
irq_trigger.trigger_irq(IrqType::Config).unwrap();
1093-
assert!(irq_trigger.has_pending_irq(IrqType::Config));
1117+
irq_trigger.trigger(VirtioInterruptType::Config).unwrap();
1118+
assert!(irq_trigger.has_pending_interrupt(VirtioInterruptType::Config));
10941119
irq_trigger.irq_status.store(0, Ordering::SeqCst);
1095-
irq_trigger.trigger_irq(IrqType::Vring).unwrap();
1096-
assert!(irq_trigger.has_pending_irq(IrqType::Vring));
1120+
irq_trigger.trigger(VirtioInterruptType::Queue(0)).unwrap();
1121+
assert!(irq_trigger.has_pending_interrupt(VirtioInterruptType::Queue(0)));
10971122

10981123
// Check trigger_irq() failure case (irq_evt is full).
10991124
irq_trigger.irq_evt.write(u64::MAX - 1).unwrap();
1100-
irq_trigger.trigger_irq(IrqType::Config).unwrap_err();
1101-
irq_trigger.trigger_irq(IrqType::Vring).unwrap_err();
1125+
irq_trigger
1126+
.trigger(VirtioInterruptType::Config)
1127+
.unwrap_err();
1128+
irq_trigger
1129+
.trigger(VirtioInterruptType::Queue(0))
1130+
.unwrap_err();
11021131
}
11031132
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
use std::sync::Arc;
5+
use std::sync::atomic::AtomicU32;
6+
7+
use vmm_sys_util::eventfd::EventFd;
8+
49
/// MMIO transport for VirtIO devices
510
pub mod mmio;
11+
12+
/// Represents the types of interrupts used by VirtIO devices
13+
#[derive(Debug, Clone)]
14+
pub enum VirtioInterruptType {
15+
/// Interrupt for VirtIO configuration changes
16+
Config,
17+
/// Interrupts for new events in a queue.
18+
Queue(u16),
19+
}
20+
21+
/// API of interrupt types used by VirtIO devices
22+
pub trait VirtioInterrupt: std::fmt::Debug + Send + Sync {
23+
/// Trigger a VirtIO interrupt.
24+
fn trigger(&self, interrupt_type: VirtioInterruptType) -> Result<(), std::io::Error>;
25+
26+
/// Get the `EventFd` (if any) that backs the underlying interrupt.
27+
fn notifier(&self, _interrupt_type: VirtioInterruptType) -> Option<&EventFd> {
28+
None
29+
}
30+
31+
/// Get the current device interrupt status.
32+
fn status(&self) -> Arc<AtomicU32>;
33+
34+
/// Returns true if there is any pending interrupt
35+
#[cfg(test)]
36+
fn has_pending_interrupt(&self, interrupt_type: VirtioInterruptType) -> bool;
37+
}

0 commit comments

Comments
 (0)