Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 74 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! rust-vmm device model.

use std::cmp::{Ord, Ordering, PartialOrd};
use std::sync::Mutex;

pub mod device_manager;
pub mod resources;
Expand Down Expand Up @@ -68,13 +69,17 @@ impl PartialOrd for IoAddress {
}
}

/// Device IO trait.
/// Device IO trait adopting interior mutability pattern.
///
/// A device supporting memory based I/O should implement this trait, then
/// register itself against the different IO type ranges it handles.
/// The VMM will then dispatch IO (PIO or MMIO) VM exits by calling into the
/// registered devices read or write method from this trait.
/// The DeviceIo trait adopts the interior mutability pattern
/// so we can get a real multiple threads handling.
///
/// The DeviceIo trait adopts the interior mutability pattern so we can get a
/// real concurrent multiple threads handling. For device backend drivers not
/// focusing on high performance, they may use the Mutex<T: DeviceIoMut>
/// adapter to simplify implementation.
pub trait DeviceIo: Send {
/// Read from the guest physical address `base`, starting at `offset`.
/// Result is placed in `data`.
Expand All @@ -83,3 +88,69 @@ pub trait DeviceIo: Send {
/// Write `data` to the guest physical address `base`, starting from `offset`.
fn write(&self, base: IoAddress, offset: IoAddress, data: &[u8]);
}

/// Device IO trait without interior mutability.
///
/// Many device backend drivers will mutate itself when handling IO requests.
/// The DeviceIo trait assumes interior mutability, but it's a little complex
/// to support interior mutability. So the Mutex<T: DeviceIoMut> adapter may be
/// used to ease device backend driver implementations.
///
/// The Mutex<T: DeviceIoMut> adapter is an zero overhead abstraction without
/// performance penalty.
pub trait DeviceIoMut: Send {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether adding the Send bound here is really necessary. What happens if we remove it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The propagation chain for Send/Sync is something like:
EpollHandler -> Vmm -> Device Manager -> IoManager -> DeviceIo

/// Read from the guest physical address `base`, starting at `offset`.
/// Result is placed in `data`.
fn read(&mut self, base: IoAddress, offset: IoAddress, data: &mut [u8]);

/// Write `data` to the guest physical address `base`, starting from `offset`.
fn write(&mut self, base: IoAddress, offset: IoAddress, data: &[u8]);
}

impl<T: DeviceIoMut> DeviceIo for Mutex<T> {
fn read(&self, base: IoAddress, offset: IoAddress, data: &mut [u8]) {
// Safe to unwrap() because we don't expect poisoned lock here.
self.lock().unwrap().read(base, offset, data)
}

fn write(&self, base: IoAddress, offset: IoAddress, data: &[u8]) {
// Safe to unwrap() because we don't expect poisoned lock here.
self.lock().unwrap().write(base, offset, data)
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;

#[derive(Default)]
struct MockDevice {
data: u8,
}

impl DeviceIoMut for MockDevice {
fn read(&mut self, _base: IoAddress, _offset: IoAddress, data: &mut [u8]) {
data[0] = self.data;
}

fn write(&mut self, _base: IoAddress, _offset: IoAddress, data: &[u8]) {
self.data = data[0];
}
}

fn register_device(device: Arc<dyn DeviceIo>) {
device.write(IoAddress::Mmio(0), IoAddress::Mmio(0), &[0x10u8]);
let mut buf = [0x0u8];
device.read(IoAddress::Mmio(0), IoAddress::Mmio(0), &mut buf);
assert_eq!(buf[0], 0x10);
}

#[test]
fn test_device_io_mut_adapter() {
let device_mut = Arc::new(Mutex::new(MockDevice::default()));

register_device(device_mut.clone());
assert_eq!(device_mut.lock().unwrap().data, 0x010);
}
}