Skip to content

Commit 2f4c51c

Browse files
committed
Introduce Mutex<T: DeviceIoMut> adapter for DeviceIo
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 introduce the Mutex<T: DeviceIoMut> adapter to ease device backend driver implementations. And the Mutex<T: DeviceIoMut> adapter is an zero overhead abstraction without performance penalty. Liu Jiang <[email protected]>
1 parent 3daea68 commit 2f4c51c

File tree

1 file changed

+74
-3
lines changed

1 file changed

+74
-3
lines changed

src/lib.rs

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! rust-vmm device model.
55
66
use std::cmp::{Ord, Ordering, PartialOrd};
7+
use std::sync::Mutex;
78

89
pub mod device_manager;
910
pub mod resources;
@@ -68,13 +69,17 @@ impl PartialOrd for IoAddress {
6869
}
6970
}
7071

71-
/// Device IO trait.
72+
/// Device IO trait adopting interior mutability pattern.
73+
///
7274
/// A device supporting memory based I/O should implement this trait, then
7375
/// register itself against the different IO type ranges it handles.
7476
/// The VMM will then dispatch IO (PIO or MMIO) VM exits by calling into the
7577
/// registered devices read or write method from this trait.
76-
/// The DeviceIo trait adopts the interior mutability pattern
77-
/// so we can get a real multiple threads handling.
78+
///
79+
/// The DeviceIo trait adopts the interior mutability pattern so we can get a
80+
/// real concurrent multiple threads handling. For device backend drivers not
81+
/// focusing on high performance, they may use the Mutex<T: DeviceIoMut>
82+
/// adapter to simplify implementation.
7883
pub trait DeviceIo: Send {
7984
/// Read from the guest physical address `base`, starting at `offset`.
8085
/// Result is placed in `data`.
@@ -83,3 +88,69 @@ pub trait DeviceIo: Send {
8388
/// Write `data` to the guest physical address `base`, starting from `offset`.
8489
fn write(&self, base: IoAddress, offset: IoAddress, data: &[u8]);
8590
}
91+
92+
/// Device IO trait without interior mutability.
93+
///
94+
/// Many device backend drivers will mutate itself when handling IO requests.
95+
/// The DeviceIo trait assumes interior mutability, but it's a little complex
96+
/// to support interior mutability. So the Mutex<T: DeviceIoMut> adapter may be
97+
/// used to ease device backend driver implementations.
98+
///
99+
/// The Mutex<T: DeviceIoMut> adapter is an zero overhead abstraction without
100+
/// performance penalty.
101+
pub trait DeviceIoMut: Send {
102+
/// Read from the guest physical address `base`, starting at `offset`.
103+
/// Result is placed in `data`.
104+
fn read(&mut self, base: IoAddress, offset: IoAddress, data: &mut [u8]);
105+
106+
/// Write `data` to the guest physical address `base`, starting from `offset`.
107+
fn write(&mut self, base: IoAddress, offset: IoAddress, data: &[u8]);
108+
}
109+
110+
impl<T: DeviceIoMut> DeviceIo for Mutex<T> {
111+
fn read(&self, base: IoAddress, offset: IoAddress, data: &mut [u8]) {
112+
// Safe to unwrap() because we don't expect poisoned lock here.
113+
self.lock().unwrap().read(base, offset, data)
114+
}
115+
116+
fn write(&self, base: IoAddress, offset: IoAddress, data: &[u8]) {
117+
// Safe to unwrap() because we don't expect poisoned lock here.
118+
self.lock().unwrap().write(base, offset, data)
119+
}
120+
}
121+
122+
#[cfg(test)]
123+
mod tests {
124+
use super::*;
125+
use std::sync::Arc;
126+
127+
#[derive(Default)]
128+
struct MockDevice {
129+
data: u8,
130+
}
131+
132+
impl DeviceIoMut for MockDevice {
133+
fn read(&mut self, _base: IoAddress, _offset: IoAddress, data: &mut [u8]) {
134+
data[0] = self.data;
135+
}
136+
137+
fn write(&mut self, _base: IoAddress, _offset: IoAddress, data: &[u8]) {
138+
self.data = data[0];
139+
}
140+
}
141+
142+
fn register_device(device: Arc<dyn DeviceIo>) {
143+
device.write(IoAddress::Mmio(0), IoAddress::Mmio(0), &[0x10u8]);
144+
let mut buf = [0x0u8];
145+
device.read(IoAddress::Mmio(0), IoAddress::Mmio(0), &mut buf);
146+
assert_eq!(buf[0], 0x10);
147+
}
148+
149+
#[test]
150+
fn test_device_io_mut_adapter() {
151+
let device_mut = Arc::new(Mutex::new(MockDevice::default()));
152+
153+
register_device(device_mut.clone());
154+
assert_eq!(device_mut.lock().unwrap().data, 0x010);
155+
}
156+
}

0 commit comments

Comments
 (0)