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,16 @@ 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 real concurrent
80+ /// multiple threads handling. For device backend drivers not focusing on high performance,
81+ /// they may use the Mutex<T: DeviceIoMut> adapter to simplify implementation.
7882pub trait DeviceIo : Send {
7983 /// Read from the guest physical address `base`, starting at `offset`.
8084 /// Result is placed in `data`.
@@ -83,3 +87,66 @@ pub trait DeviceIo: Send {
8387 /// Write `data` to the guest physical address `base`, starting from `offset`.
8488 fn write ( & self , base : IoAddress , offset : IoAddress , data : & [ u8 ] ) ;
8589}
90+
91+ /// Device IO trait without interior mutability.
92+ ///
93+ /// Many device backend drivers will mutate itself when handling IO requests. The DeviceIo trait
94+ /// assumes interior mutability, but it's a little complex to support interior mutability. So
95+ /// the Mutex<T: DeviceIoMut> adapter may be used to ease device backend driver implementations.
96+ /// The Mutex<T: DeviceIoMut> adapter is an zero overhead abstraction without performance penalty.
97+ pub trait DeviceIoMut : Send {
98+ /// Read from the guest physical address `base`, starting at `offset`.
99+ /// Result is placed in `data`.
100+ fn read ( & mut self , base : IoAddress , offset : IoAddress , data : & mut [ u8 ] ) ;
101+
102+ /// Write `data` to the guest physical address `base`, starting from `offset`.
103+ fn write ( & mut self , base : IoAddress , offset : IoAddress , data : & [ u8 ] ) ;
104+ }
105+
106+ impl < T : DeviceIoMut > DeviceIo for Mutex < T > {
107+ fn read ( & self , base : IoAddress , offset : IoAddress , data : & mut [ u8 ] ) {
108+ // Safe to unwrap() because we don't expect poisoned lock here.
109+ self . lock ( ) . unwrap ( ) . read ( base, offset, data)
110+ }
111+
112+ fn write ( & self , base : IoAddress , offset : IoAddress , data : & [ u8 ] ) {
113+ // Safe to unwrap() because we don't expect poisoned lock here.
114+ self . lock ( ) . unwrap ( ) . write ( base, offset, data)
115+ }
116+ }
117+
118+ #[ cfg( test) ]
119+ mod tests {
120+ use super :: * ;
121+ use std:: sync:: Arc ;
122+
123+ #[ derive( Default ) ]
124+ struct MockDevice {
125+ data : u8 ,
126+ }
127+
128+ impl DeviceIoMut for MockDevice {
129+ fn read ( & mut self , _base : IoAddress , _offset : IoAddress , data : & mut [ u8 ] ) {
130+ data[ 0 ] = self . data ;
131+ }
132+
133+ fn write ( & mut self , _base : IoAddress , _offset : IoAddress , data : & [ u8 ] ) {
134+ self . data = data[ 0 ] ;
135+ }
136+ }
137+
138+ fn register_device ( device : Arc < dyn DeviceIo > ) {
139+ device. write ( IoAddress :: Mmio ( 0 ) , IoAddress :: Mmio ( 0 ) , & [ 0x10u8 ] ) ;
140+ let mut buf = [ 0x0u8 ] ;
141+ device. read ( IoAddress :: Mmio ( 0 ) , IoAddress :: Mmio ( 0 ) , & mut buf) ;
142+ assert_eq ! ( buf[ 0 ] , 0x10 ) ;
143+ }
144+
145+ #[ test]
146+ fn test_device_io_mut_adapter ( ) {
147+ let device_mut = Arc :: new ( Mutex :: new ( MockDevice :: default ( ) ) ) ;
148+
149+ register_device ( device_mut. clone ( ) ) ;
150+ assert_eq ! ( device_mut. lock( ) . unwrap( ) . data, 0x010 ) ;
151+ }
152+ }
0 commit comments