Skip to content

Commit fb4696d

Browse files
author
Alexandru-Cezar Sardan
committed
examples: add example for interrupt usage
Add a simple example showing how the interrupt traits can be used. Signed-off-by: Alexandru-Cezar Sardan <alsardan@amazon.com>
1 parent b2df144 commit fb4696d

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ license = "Apache-2.0 OR BSD-3-Clause"
1010

1111
[dependencies]
1212
vmm-sys-util = { version = "0.8.0" }
13+
14+
[dev-dependencies]
15+
libc = {version = "0.2.100" }

examples/interrupts/device.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use std::sync::{Arc, Mutex};
2+
use vm_device::interrupt::{Interrupt, InterruptSourceGroup};
3+
4+
type IntGroup<I> = dyn InterruptSourceGroup<InterruptType = I, InterruptWrapper = Arc<I>>;
5+
6+
/// Example of the internal state of a device
7+
pub struct Device<I: Interrupt> {
8+
/// A device should hold a reference to the interrupt group
9+
_interrupt_group: Arc<Mutex<IntGroup<I>>>,
10+
/// Optionally, a device may store references to the interrupts for easy access
11+
irq0: Arc<I>,
12+
}
13+
14+
impl<I: Interrupt> Device<I> {
15+
/// Creates a new device.
16+
/// The interrupt group that the device will use is passed as an argument by the caller.
17+
pub fn new(interrupt_group: Arc<Mutex<IntGroup<I>>>) -> Result<Self, &'static str> {
18+
let mut irq_group = interrupt_group.lock().expect("Poisoned Lock");
19+
// Allocate an interrupt. In this case the device uses a single interrupt.
20+
irq_group
21+
.allocate_interrupts(1)
22+
.map_err(|_| "Cannot allocate interrupts.")?;
23+
// Save the reference to the interrupt for easy access later.
24+
let irq = irq_group.get(0).ok_or("Can't find interrupt with id 0.")?;
25+
std::mem::drop(irq_group);
26+
27+
Ok(Device {
28+
_interrupt_group: interrupt_group,
29+
irq0: irq,
30+
})
31+
}
32+
33+
/// Start the device
34+
pub fn start(&self) -> Result<(), &'static str> {
35+
// Enable interrupt generation for this device
36+
self.irq0
37+
.enable()
38+
.map_err(|_| "Cannot enable interrupts.")?;
39+
Ok(())
40+
}
41+
42+
/// Do some device speciffic work
43+
pub fn run(&self) -> Result<(), &'static str> {
44+
// Do some work...
45+
46+
// Trigger an interrupt when work is finished
47+
Ok(self
48+
.irq0
49+
.trigger()
50+
.map_err(|_| "Cannot trigger the interrupt.")?)
51+
}
52+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use std::sync::{atomic::AtomicBool, atomic::Ordering, Arc};
2+
use vm_device::interrupt::trigger::{eventfd::EventFdTrigger, Trigger};
3+
use vm_device::interrupt::{Interrupt, InterruptSourceGroup, Result};
4+
use vmm_sys_util::eventfd::EventFd;
5+
extern crate libc;
6+
7+
/// Defines an implementation for a VMM speciffic interrupt type
8+
pub struct CustomInterrupt {
9+
trigger: EventFdTrigger,
10+
enabled: AtomicBool,
11+
}
12+
13+
impl Interrupt for CustomInterrupt {
14+
type TriggerType = EventFdTrigger;
15+
16+
fn trigger(&self) -> std::result::Result<(), <Self::TriggerType as Trigger>::E> {
17+
if self.enabled.load(Ordering::Acquire) {
18+
self.trigger.trigger()
19+
} else {
20+
Err(std::io::Error::from_raw_os_error(libc::EINVAL))
21+
}
22+
}
23+
24+
/// Return the underlying trigger
25+
fn notifier(&self) -> Option<Self::TriggerType> {
26+
Some(self.trigger.try_clone().unwrap())
27+
}
28+
29+
/// Enable generation of interrupts from this interrupt source.
30+
fn enable(&self) -> Result<()> {
31+
// Change the state of the interrupt to enabled.
32+
// In other implementation, here is the place that the event
33+
// may be registered with listeners (e.g. register irqfd with KVM)
34+
// For interrupt tha should be enabled as a group and not idividually
35+
// e.g. MSI interrupts, this method should return OperationNotSupported.
36+
self.enabled.store(true, Ordering::Release);
37+
Ok(())
38+
}
39+
40+
/// Disable generation of interrupts from this interrupt source.
41+
fn disable(&self) -> Result<()> {
42+
// Change the state of the interrupt to disabled.
43+
self.enabled.store(false, Ordering::Release);
44+
Ok(())
45+
}
46+
}
47+
48+
/// The custom interrupt group that all the interrupts for the device
49+
pub struct CustomInterruptGroup {
50+
interrupts: Vec<Arc<CustomInterrupt>>,
51+
}
52+
53+
impl CustomInterruptGroup {
54+
pub fn new() -> Self {
55+
CustomInterruptGroup {
56+
interrupts: Vec::new(),
57+
}
58+
}
59+
}
60+
61+
impl InterruptSourceGroup for CustomInterruptGroup {
62+
/// Type of the interrupts contained in this group.
63+
type InterruptType = CustomInterrupt;
64+
/// Interrupts are wrapped in an Arc to allow for easy access when triggering.
65+
type InterruptWrapper = Arc<CustomInterrupt>;
66+
67+
fn is_empty(&self) -> bool {
68+
self.interrupts.is_empty()
69+
}
70+
71+
fn len(&self) -> usize {
72+
self.interrupts.len()
73+
}
74+
75+
fn enable(&self) -> Result<()> {
76+
// For interrupts that should be enabled as a group (e.g. MSI), this method
77+
// should coordinate enabling of all interrupts using methods from
78+
// CustomInterrupt instead of the Interrupt trait.
79+
Ok(for i in &self.interrupts {
80+
i.enable().unwrap()
81+
})
82+
}
83+
84+
fn disable(&self) -> Result<()> {
85+
Ok(for i in &self.interrupts {
86+
i.disable().unwrap()
87+
})
88+
}
89+
90+
fn get(&self, index: usize) -> Option<Self::InterruptWrapper> {
91+
match self.interrupts.get(index) {
92+
Some(interrupt) => Some(interrupt.clone()),
93+
None => None,
94+
}
95+
}
96+
97+
/// Request new interrupts within this group.
98+
fn allocate_interrupts(&mut self, size: usize) -> Result<()> {
99+
for _ in 0..size {
100+
let irq_fd = EventFd::new(libc::EFD_NONBLOCK)?;
101+
let interrupt = CustomInterrupt {
102+
trigger: EventFdTrigger::new(irq_fd),
103+
enabled: AtomicBool::new(false),
104+
};
105+
self.interrupts.push(Arc::new(interrupt));
106+
}
107+
Ok(())
108+
}
109+
110+
/// Release all interrupts within this group.
111+
fn free_interrupts(&mut self) -> Result<()> {
112+
self.interrupts.clear();
113+
Ok(())
114+
}
115+
}

examples/interrupts/main.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
mod device;
2+
mod interrupt_manager;
3+
4+
use device::Device;
5+
use interrupt_manager::CustomInterruptGroup;
6+
use std::error::Error;
7+
use std::sync::{Arc, Mutex};
8+
use vm_device::interrupt::{Interrupt, InterruptSourceGroup};
9+
10+
fn main() -> Result<(), Box<dyn Error>> {
11+
// Create an interrupt group for the device
12+
let interrupt_group = Arc::new(Mutex::new(CustomInterruptGroup::new()));
13+
// Pass the interrupt group to the device
14+
let device = Device::new(interrupt_group.clone())?;
15+
16+
device.start()?;
17+
device.run()?; // The device will do some work and send an interrupt
18+
19+
let locked_group = interrupt_group.lock().expect("Poisoned Lock");
20+
21+
// This interrupt is using an eventfd
22+
// so we can read from it to see if the device triggered an interrupt
23+
let irq = locked_group.get(0).ok_or("Cannot get irq.")?;
24+
let trigger = irq.notifier().ok_or("Cannot get trigger.")?;
25+
let evt = trigger
26+
.get_event()
27+
.map_err(|_| "Cannot get eventfd from trigger.")?;
28+
match evt.read() {
29+
Ok(data) => println!("Received interrupt with data: {}", data),
30+
Err(_) => println!("No interrupt!"),
31+
};
32+
Ok(())
33+
}

0 commit comments

Comments
 (0)