@@ -9,7 +9,7 @@ use std::sync::atomic::AtomicU32;
9
9
use log:: info;
10
10
use serde:: { Deserialize , Serialize } ;
11
11
use vm_memory:: {
12
- Address , GuestAddress , GuestMemory , GuestMemoryError , GuestMemoryRegion , GuestUsize ,
12
+ Address , Bytes , GuestAddress , GuestMemory , GuestMemoryError , GuestMemoryRegion , GuestUsize ,
13
13
} ;
14
14
use vmm_sys_util:: eventfd:: EventFd ;
15
15
@@ -20,12 +20,15 @@ use crate::devices::virtio::device::{ActiveState, DeviceState, VirtioDevice};
20
20
use crate :: devices:: virtio:: generated:: virtio_config:: VIRTIO_F_VERSION_1 ;
21
21
use crate :: devices:: virtio:: generated:: virtio_ids:: VIRTIO_ID_MEM ;
22
22
use crate :: devices:: virtio:: generated:: virtio_mem:: {
23
- VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE , virtio_mem_config,
23
+ self , VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE , virtio_mem_config,
24
24
} ;
25
25
use crate :: devices:: virtio:: iov_deque:: IovDequeError ;
26
26
use crate :: devices:: virtio:: mem:: metrics:: METRICS ;
27
+ use crate :: devices:: virtio:: mem:: request:: { BlockRangeState , Request , RequestedRange , Response } ;
27
28
use crate :: devices:: virtio:: mem:: { VIRTIO_MEM_DEV_ID , VIRTIO_MEM_GUEST_ADDRESS } ;
28
- use crate :: devices:: virtio:: queue:: { FIRECRACKER_MAX_QUEUE_SIZE , InvalidAvailIdx , Queue } ;
29
+ use crate :: devices:: virtio:: queue:: {
30
+ DescriptorChain , FIRECRACKER_MAX_QUEUE_SIZE , InvalidAvailIdx , Queue , QueueError ,
31
+ } ;
29
32
use crate :: devices:: virtio:: transport:: { VirtioInterrupt , VirtioInterruptType } ;
30
33
use crate :: logger:: { IncMetric , debug, error} ;
31
34
use crate :: utils:: { bytes_to_mib, mib_to_bytes, u64_to_usize, usize_to_u64} ;
@@ -47,6 +50,24 @@ pub enum VirtioMemError {
47
50
InvalidSize ( u64 ) ,
48
51
/// Device is not active
49
52
DeviceNotActive ,
53
+ /// Descriptor is write-only
54
+ UnexpectedWriteOnlyDescriptor ,
55
+ /// Error reading virtio descriptor
56
+ DescriptorWriteFailed ,
57
+ /// Error writing virtio descriptor
58
+ DescriptorReadFailed ,
59
+ /// Unknown request type: {0:?}
60
+ UnknownRequestType ( u32 ) ,
61
+ /// Descriptor chain is too short
62
+ DescriptorChainTooShort ,
63
+ /// Descriptor is too small
64
+ DescriptorLengthTooSmall ,
65
+ /// Descriptor is read-only
66
+ UnexpectedReadOnlyDescriptor ,
67
+ /// Error popping from virtio queue: {0}
68
+ InvalidAvailIdx ( #[ from] InvalidAvailIdx ) ,
69
+ /// Error adding used queue: {0}
70
+ QueueError ( #[ from] QueueError ) ,
50
71
}
51
72
52
73
#[ derive( Debug ) ]
@@ -170,8 +191,139 @@ impl VirtioMem {
170
191
. map_err ( VirtioMemError :: InterruptError )
171
192
}
172
193
194
+ fn guest_memory ( & self ) -> & GuestMemoryMmap {
195
+ & self . device_state . active_state ( ) . unwrap ( ) . mem
196
+ }
197
+
198
+ fn parse_request (
199
+ & self ,
200
+ avail_desc : & DescriptorChain ,
201
+ ) -> Result < ( Request , GuestAddress , u16 ) , VirtioMemError > {
202
+ // The head contains the request type which MUST be readable.
203
+ if avail_desc. is_write_only ( ) {
204
+ return Err ( VirtioMemError :: UnexpectedWriteOnlyDescriptor ) ;
205
+ }
206
+
207
+ if ( avail_desc. len as usize ) < size_of :: < virtio_mem:: virtio_mem_req > ( ) {
208
+ return Err ( VirtioMemError :: DescriptorLengthTooSmall ) ;
209
+ }
210
+
211
+ let request: virtio_mem:: virtio_mem_req = self
212
+ . guest_memory ( )
213
+ . read_obj ( avail_desc. addr )
214
+ . map_err ( |_| VirtioMemError :: DescriptorReadFailed ) ?;
215
+
216
+ let resp_desc = avail_desc
217
+ . next_descriptor ( )
218
+ . ok_or ( VirtioMemError :: DescriptorChainTooShort ) ?;
219
+
220
+ // The response MUST always be writable.
221
+ if !resp_desc. is_write_only ( ) {
222
+ return Err ( VirtioMemError :: UnexpectedReadOnlyDescriptor ) ;
223
+ }
224
+
225
+ if ( resp_desc. len as usize ) < std:: mem:: size_of :: < virtio_mem:: virtio_mem_resp > ( ) {
226
+ return Err ( VirtioMemError :: DescriptorLengthTooSmall ) ;
227
+ }
228
+
229
+ Ok ( ( request. into ( ) , resp_desc. addr , avail_desc. index ) )
230
+ }
231
+
232
+ fn write_response (
233
+ & mut self ,
234
+ resp : Response ,
235
+ resp_addr : GuestAddress ,
236
+ used_idx : u16 ,
237
+ ) -> Result < ( ) , VirtioMemError > {
238
+ debug ! ( "virtio-mem: Response: {:?}" , resp) ;
239
+ self . guest_memory ( )
240
+ . write_obj ( virtio_mem:: virtio_mem_resp:: from ( resp) , resp_addr)
241
+ . map_err ( |_| VirtioMemError :: DescriptorWriteFailed )
242
+ . map ( |_| size_of :: < virtio_mem:: virtio_mem_resp > ( ) ) ?;
243
+ self . queues [ MEM_QUEUE ]
244
+ . add_used (
245
+ used_idx,
246
+ u32:: try_from ( std:: mem:: size_of :: < virtio_mem:: virtio_mem_resp > ( ) ) . unwrap ( ) ,
247
+ )
248
+ . map_err ( VirtioMemError :: QueueError )
249
+ }
250
+
251
+ fn handle_plug_request (
252
+ & mut self ,
253
+ range : & RequestedRange ,
254
+ resp_addr : GuestAddress ,
255
+ used_idx : u16 ,
256
+ ) -> Result < ( ) , VirtioMemError > {
257
+ METRICS . plug_count . inc ( ) ;
258
+ let _metric = METRICS . plug_agg . record_latency_metrics ( ) ;
259
+
260
+ // TODO: implement PLUG request
261
+ let response = Response :: ack ( ) ;
262
+ self . write_response ( response, resp_addr, used_idx)
263
+ }
264
+
265
+ fn handle_unplug_request (
266
+ & mut self ,
267
+ range : & RequestedRange ,
268
+ resp_addr : GuestAddress ,
269
+ used_idx : u16 ,
270
+ ) -> Result < ( ) , VirtioMemError > {
271
+ METRICS . unplug_count . inc ( ) ;
272
+ let _metric = METRICS . unplug_agg . record_latency_metrics ( ) ;
273
+
274
+ // TODO: implement UNPLUG request
275
+ let response = Response :: ack ( ) ;
276
+ self . write_response ( response, resp_addr, used_idx)
277
+ }
278
+
279
+ fn handle_unplug_all_request (
280
+ & mut self ,
281
+ resp_addr : GuestAddress ,
282
+ used_idx : u16 ,
283
+ ) -> Result < ( ) , VirtioMemError > {
284
+ METRICS . unplug_all_count . inc ( ) ;
285
+ let _metric = METRICS . unplug_all_agg . record_latency_metrics ( ) ;
286
+
287
+ // TODO: implement UNPLUG ALL request
288
+ let response = Response :: ack ( ) ;
289
+ self . write_response ( response, resp_addr, used_idx)
290
+ }
291
+
292
+ fn handle_state_request (
293
+ & mut self ,
294
+ range : & RequestedRange ,
295
+ resp_addr : GuestAddress ,
296
+ used_idx : u16 ,
297
+ ) -> Result < ( ) , VirtioMemError > {
298
+ METRICS . state_count . inc ( ) ;
299
+ let _metric = METRICS . state_agg . record_latency_metrics ( ) ;
300
+
301
+ // TODO: implement STATE request
302
+ let response = Response :: ack_with_state ( BlockRangeState :: Mixed ) ;
303
+ self . write_response ( response, resp_addr, used_idx)
304
+ }
305
+
173
306
fn process_mem_queue ( & mut self ) -> Result < ( ) , VirtioMemError > {
174
- info ! ( "TODO: Received mem queue event, but it's not implemented." ) ;
307
+ while let Some ( desc) = self . queues [ MEM_QUEUE ] . pop ( ) ? {
308
+ let index = desc. index ;
309
+
310
+ let ( req, resp_addr, used_idx) = self . parse_request ( & desc) ?;
311
+ debug ! ( "virtio-mem: Request: {:?}" , req) ;
312
+ // Handle request and write response
313
+ match req {
314
+ Request :: State ( ref range) => self . handle_state_request ( range, resp_addr, used_idx) ,
315
+ Request :: Plug ( ref range) => self . handle_plug_request ( range, resp_addr, used_idx) ,
316
+ Request :: Unplug ( ref range) => {
317
+ self . handle_unplug_request ( range, resp_addr, used_idx)
318
+ }
319
+ Request :: UnplugAll => self . handle_unplug_all_request ( resp_addr, used_idx) ,
320
+ Request :: Unsupported ( t) => Err ( VirtioMemError :: UnknownRequestType ( t) ) ,
321
+ } ?;
322
+ }
323
+
324
+ self . queues [ MEM_QUEUE ] . advance_used_ring_idx ( ) ;
325
+ self . signal_used_queue ( ) ?;
326
+
175
327
Ok ( ( ) )
176
328
}
177
329
@@ -237,11 +389,9 @@ impl VirtioMem {
237
389
"virtio-mem: Updated requested size to {} bytes" ,
238
390
requested_size
239
391
) ;
240
- // TODO(virtio-mem): trigger interrupt once we add handling for the requests
241
- // self.interrupt_trigger()
242
- // .trigger(VirtioInterruptType::Config)
243
- // .map_err(VirtioMemError::InterruptError)
244
- Ok ( ( ) )
392
+ self . interrupt_trigger ( )
393
+ . trigger ( VirtioInterruptType :: Config )
394
+ . map_err ( VirtioMemError :: InterruptError )
245
395
}
246
396
}
247
397
0 commit comments