Skip to content

Commit c01e869

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 5524aba commit c01e869

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 {
@@ -1062,44 +1106,29 @@ pub(crate) mod tests {
10621106
assert_eq!(dummy_dev.acked_features(), 24);
10631107
}
10641108

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

10891114
// Check that there are no pending irqs.
1090-
assert!(!irq_trigger.has_pending_irq(IrqType::Config));
1091-
assert!(!irq_trigger.has_pending_irq(IrqType::Vring));
1115+
assert!(!irq_trigger.has_pending_interrupt(VirtioInterruptType::Config));
1116+
assert!(!irq_trigger.has_pending_interrupt(VirtioInterruptType::Queue(0)));
10921117

10931118
// Check that trigger_irq() correctly generates irqs.
1094-
irq_trigger.trigger_irq(IrqType::Config).unwrap();
1095-
assert!(irq_trigger.has_pending_irq(IrqType::Config));
1119+
irq_trigger.trigger(VirtioInterruptType::Config).unwrap();
1120+
assert!(irq_trigger.has_pending_interrupt(VirtioInterruptType::Config));
10961121
irq_trigger.irq_status.store(0, Ordering::SeqCst);
1097-
irq_trigger.trigger_irq(IrqType::Vring).unwrap();
1098-
assert!(irq_trigger.has_pending_irq(IrqType::Vring));
1122+
irq_trigger.trigger(VirtioInterruptType::Queue(0)).unwrap();
1123+
assert!(irq_trigger.has_pending_interrupt(VirtioInterruptType::Queue(0)));
10991124

11001125
// Check trigger_irq() failure case (irq_evt is full).
11011126
irq_trigger.irq_evt.write(u64::MAX - 1).unwrap();
1102-
irq_trigger.trigger_irq(IrqType::Config).unwrap_err();
1103-
irq_trigger.trigger_irq(IrqType::Vring).unwrap_err();
1127+
irq_trigger
1128+
.trigger(VirtioInterruptType::Config)
1129+
.unwrap_err();
1130+
irq_trigger
1131+
.trigger(VirtioInterruptType::Queue(0))
1132+
.unwrap_err();
11041133
}
11051134
}
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)