44//! rust-vmm device model.
55
66use std:: cmp:: { Ord , Ordering , PartialOrd } ;
7+ use std:: sync:: Mutex ;
78
89pub mod device_manager;
910pub 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.
7883pub 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