-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
examples: add example for interrupt usage
Add a simple example showing how the interrupt traits can be used. Signed-off-by: Alexandru-Cezar Sardan <[email protected]>
- Loading branch information
Alexandru-Cezar Sardan
committed
Jan 25, 2022
1 parent
b2df144
commit fb4696d
Showing
4 changed files
with
203 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use std::sync::{Arc, Mutex}; | ||
use vm_device::interrupt::{Interrupt, InterruptSourceGroup}; | ||
|
||
type IntGroup<I> = dyn InterruptSourceGroup<InterruptType = I, InterruptWrapper = Arc<I>>; | ||
|
||
/// Example of the internal state of a device | ||
pub struct Device<I: Interrupt> { | ||
/// A device should hold a reference to the interrupt group | ||
_interrupt_group: Arc<Mutex<IntGroup<I>>>, | ||
/// Optionally, a device may store references to the interrupts for easy access | ||
irq0: Arc<I>, | ||
} | ||
|
||
impl<I: Interrupt> Device<I> { | ||
/// Creates a new device. | ||
/// The interrupt group that the device will use is passed as an argument by the caller. | ||
pub fn new(interrupt_group: Arc<Mutex<IntGroup<I>>>) -> Result<Self, &'static str> { | ||
let mut irq_group = interrupt_group.lock().expect("Poisoned Lock"); | ||
// Allocate an interrupt. In this case the device uses a single interrupt. | ||
irq_group | ||
.allocate_interrupts(1) | ||
.map_err(|_| "Cannot allocate interrupts.")?; | ||
// Save the reference to the interrupt for easy access later. | ||
let irq = irq_group.get(0).ok_or("Can't find interrupt with id 0.")?; | ||
std::mem::drop(irq_group); | ||
|
||
Ok(Device { | ||
_interrupt_group: interrupt_group, | ||
irq0: irq, | ||
}) | ||
} | ||
|
||
/// Start the device | ||
pub fn start(&self) -> Result<(), &'static str> { | ||
// Enable interrupt generation for this device | ||
self.irq0 | ||
.enable() | ||
.map_err(|_| "Cannot enable interrupts.")?; | ||
Ok(()) | ||
} | ||
|
||
/// Do some device speciffic work | ||
pub fn run(&self) -> Result<(), &'static str> { | ||
// Do some work... | ||
|
||
// Trigger an interrupt when work is finished | ||
Ok(self | ||
.irq0 | ||
.trigger() | ||
.map_err(|_| "Cannot trigger the interrupt.")?) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
use std::sync::{atomic::AtomicBool, atomic::Ordering, Arc}; | ||
use vm_device::interrupt::trigger::{eventfd::EventFdTrigger, Trigger}; | ||
use vm_device::interrupt::{Interrupt, InterruptSourceGroup, Result}; | ||
use vmm_sys_util::eventfd::EventFd; | ||
extern crate libc; | ||
|
||
/// Defines an implementation for a VMM speciffic interrupt type | ||
pub struct CustomInterrupt { | ||
trigger: EventFdTrigger, | ||
enabled: AtomicBool, | ||
} | ||
|
||
impl Interrupt for CustomInterrupt { | ||
type TriggerType = EventFdTrigger; | ||
|
||
fn trigger(&self) -> std::result::Result<(), <Self::TriggerType as Trigger>::E> { | ||
if self.enabled.load(Ordering::Acquire) { | ||
self.trigger.trigger() | ||
} else { | ||
Err(std::io::Error::from_raw_os_error(libc::EINVAL)) | ||
} | ||
} | ||
|
||
/// Return the underlying trigger | ||
fn notifier(&self) -> Option<Self::TriggerType> { | ||
Some(self.trigger.try_clone().unwrap()) | ||
} | ||
|
||
/// Enable generation of interrupts from this interrupt source. | ||
fn enable(&self) -> Result<()> { | ||
// Change the state of the interrupt to enabled. | ||
// In other implementation, here is the place that the event | ||
// may be registered with listeners (e.g. register irqfd with KVM) | ||
// For interrupt tha should be enabled as a group and not idividually | ||
// e.g. MSI interrupts, this method should return OperationNotSupported. | ||
self.enabled.store(true, Ordering::Release); | ||
Ok(()) | ||
} | ||
|
||
/// Disable generation of interrupts from this interrupt source. | ||
fn disable(&self) -> Result<()> { | ||
// Change the state of the interrupt to disabled. | ||
self.enabled.store(false, Ordering::Release); | ||
Ok(()) | ||
} | ||
} | ||
|
||
/// The custom interrupt group that all the interrupts for the device | ||
pub struct CustomInterruptGroup { | ||
interrupts: Vec<Arc<CustomInterrupt>>, | ||
} | ||
|
||
impl CustomInterruptGroup { | ||
pub fn new() -> Self { | ||
CustomInterruptGroup { | ||
interrupts: Vec::new(), | ||
} | ||
} | ||
} | ||
|
||
impl InterruptSourceGroup for CustomInterruptGroup { | ||
/// Type of the interrupts contained in this group. | ||
type InterruptType = CustomInterrupt; | ||
/// Interrupts are wrapped in an Arc to allow for easy access when triggering. | ||
type InterruptWrapper = Arc<CustomInterrupt>; | ||
|
||
fn is_empty(&self) -> bool { | ||
self.interrupts.is_empty() | ||
} | ||
|
||
fn len(&self) -> usize { | ||
self.interrupts.len() | ||
} | ||
|
||
fn enable(&self) -> Result<()> { | ||
// For interrupts that should be enabled as a group (e.g. MSI), this method | ||
// should coordinate enabling of all interrupts using methods from | ||
// CustomInterrupt instead of the Interrupt trait. | ||
Ok(for i in &self.interrupts { | ||
i.enable().unwrap() | ||
}) | ||
} | ||
|
||
fn disable(&self) -> Result<()> { | ||
Ok(for i in &self.interrupts { | ||
i.disable().unwrap() | ||
}) | ||
} | ||
|
||
fn get(&self, index: usize) -> Option<Self::InterruptWrapper> { | ||
match self.interrupts.get(index) { | ||
Some(interrupt) => Some(interrupt.clone()), | ||
None => None, | ||
} | ||
} | ||
|
||
/// Request new interrupts within this group. | ||
fn allocate_interrupts(&mut self, size: usize) -> Result<()> { | ||
for _ in 0..size { | ||
let irq_fd = EventFd::new(libc::EFD_NONBLOCK)?; | ||
let interrupt = CustomInterrupt { | ||
trigger: EventFdTrigger::new(irq_fd), | ||
enabled: AtomicBool::new(false), | ||
}; | ||
self.interrupts.push(Arc::new(interrupt)); | ||
} | ||
Ok(()) | ||
} | ||
|
||
/// Release all interrupts within this group. | ||
fn free_interrupts(&mut self) -> Result<()> { | ||
self.interrupts.clear(); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
mod device; | ||
mod interrupt_manager; | ||
|
||
use device::Device; | ||
use interrupt_manager::CustomInterruptGroup; | ||
use std::error::Error; | ||
use std::sync::{Arc, Mutex}; | ||
use vm_device::interrupt::{Interrupt, InterruptSourceGroup}; | ||
|
||
fn main() -> Result<(), Box<dyn Error>> { | ||
// Create an interrupt group for the device | ||
let interrupt_group = Arc::new(Mutex::new(CustomInterruptGroup::new())); | ||
// Pass the interrupt group to the device | ||
let device = Device::new(interrupt_group.clone())?; | ||
|
||
device.start()?; | ||
device.run()?; // The device will do some work and send an interrupt | ||
|
||
let locked_group = interrupt_group.lock().expect("Poisoned Lock"); | ||
|
||
// This interrupt is using an eventfd | ||
// so we can read from it to see if the device triggered an interrupt | ||
let irq = locked_group.get(0).ok_or("Cannot get irq.")?; | ||
let trigger = irq.notifier().ok_or("Cannot get trigger.")?; | ||
let evt = trigger | ||
.get_event() | ||
.map_err(|_| "Cannot get eventfd from trigger.")?; | ||
match evt.read() { | ||
Ok(data) => println!("Received interrupt with data: {}", data), | ||
Err(_) => println!("No interrupt!"), | ||
}; | ||
Ok(()) | ||
} |