@@ -11,7 +11,7 @@ use volatile::Volatile;
11
11
/// The mechanism for bulk data transport on virtio devices.
12
12
///
13
13
/// Each device can have zero or more virtqueues.
14
- #[ repr ( C ) ]
14
+ #[ derive ( Debug ) ]
15
15
pub struct VirtQueue < ' a , H : Hal > {
16
16
/// DMA guard
17
17
dma : DMA < H > ,
@@ -24,7 +24,10 @@ pub struct VirtQueue<'a, H: Hal> {
24
24
25
25
/// The index of queue
26
26
queue_idx : u32 ,
27
- /// The size of queue
27
+ /// The size of the queue.
28
+ ///
29
+ /// This is both the number of descriptors, and the number of slots in the available and used
30
+ /// rings.
28
31
queue_size : u16 ,
29
32
/// The number of used queues.
30
33
num_used : u16 ,
@@ -44,7 +47,7 @@ impl<H: Hal> VirtQueue<'_, H> {
44
47
return Err ( Error :: InvalidParam ) ;
45
48
}
46
49
let layout = VirtQueueLayout :: new ( size) ;
47
- // alloc continuous pages
50
+ // Allocate contiguous pages.
48
51
let dma = DMA :: new ( layout. size / PAGE_SIZE ) ?;
49
52
50
53
header. queue_set ( idx as u32 , size as u32 , PAGE_SIZE as u32 , dma. pfn ( ) ) ;
@@ -54,7 +57,7 @@ impl<H: Hal> VirtQueue<'_, H> {
54
57
let avail = unsafe { & mut * ( ( dma. vaddr ( ) + layout. avail_offset ) as * mut AvailRing ) } ;
55
58
let used = unsafe { & mut * ( ( dma. vaddr ( ) + layout. used_offset ) as * mut UsedRing ) } ;
56
59
57
- // link descriptors together
60
+ // Link descriptors together.
58
61
for i in 0 ..( size - 1 ) {
59
62
desc[ i as usize ] . next . write ( i + 1 ) ;
60
63
}
@@ -260,3 +263,102 @@ struct UsedElem {
260
263
id : Volatile < u32 > ,
261
264
len : Volatile < u32 > ,
262
265
}
266
+
267
+ #[ cfg( test) ]
268
+ mod tests {
269
+ use super :: * ;
270
+ use crate :: hal:: fake:: FakeHal ;
271
+ use core:: mem:: zeroed;
272
+
273
+ #[ test]
274
+ fn invalid_queue_size ( ) {
275
+ let mut header = unsafe { zeroed ( ) } ;
276
+ // Size not a power of 2.
277
+ assert_eq ! (
278
+ VirtQueue :: <FakeHal >:: new( & mut header, 0 , 3 ) . unwrap_err( ) ,
279
+ Error :: InvalidParam
280
+ ) ;
281
+ }
282
+
283
+ #[ test]
284
+ fn queue_too_big ( ) {
285
+ let mut header = VirtIOHeader :: make_fake_header ( 0 , 0 , 0 , 4 ) ;
286
+ assert_eq ! (
287
+ VirtQueue :: <FakeHal >:: new( & mut header, 0 , 5 ) . unwrap_err( ) ,
288
+ Error :: InvalidParam
289
+ ) ;
290
+ }
291
+
292
+ #[ test]
293
+ fn queue_already_used ( ) {
294
+ let mut header = VirtIOHeader :: make_fake_header ( 0 , 0 , 0 , 4 ) ;
295
+ VirtQueue :: < FakeHal > :: new ( & mut header, 0 , 4 ) . unwrap ( ) ;
296
+ assert_eq ! (
297
+ VirtQueue :: <FakeHal >:: new( & mut header, 0 , 4 ) . unwrap_err( ) ,
298
+ Error :: AlreadyUsed
299
+ ) ;
300
+ }
301
+
302
+ #[ test]
303
+ fn add_empty ( ) {
304
+ let mut header = VirtIOHeader :: make_fake_header ( 0 , 0 , 0 , 4 ) ;
305
+ let mut queue = VirtQueue :: < FakeHal > :: new ( & mut header, 0 , 4 ) . unwrap ( ) ;
306
+ assert_eq ! ( queue. add( & [ ] , & [ ] ) . unwrap_err( ) , Error :: InvalidParam ) ;
307
+ }
308
+
309
+ #[ test]
310
+ fn add_too_big ( ) {
311
+ let mut header = VirtIOHeader :: make_fake_header ( 0 , 0 , 0 , 4 ) ;
312
+ let mut queue = VirtQueue :: < FakeHal > :: new ( & mut header, 0 , 4 ) . unwrap ( ) ;
313
+ assert_eq ! ( queue. available_desc( ) , 4 ) ;
314
+ assert_eq ! (
315
+ queue
316
+ . add( & [ & [ ] , & [ ] , & [ ] ] , & [ & mut [ ] , & mut [ ] ] )
317
+ . unwrap_err( ) ,
318
+ Error :: BufferTooSmall
319
+ ) ;
320
+ }
321
+
322
+ #[ test]
323
+ fn add_buffers ( ) {
324
+ let mut header = VirtIOHeader :: make_fake_header ( 0 , 0 , 0 , 4 ) ;
325
+ let mut queue = VirtQueue :: < FakeHal > :: new ( & mut header, 0 , 4 ) . unwrap ( ) ;
326
+ assert_eq ! ( queue. size( ) , 4 ) ;
327
+ assert_eq ! ( queue. available_desc( ) , 4 ) ;
328
+
329
+ // Add a buffer chain consisting of two device-readable parts followed by two
330
+ // device-writable parts.
331
+ let token = queue
332
+ . add ( & [ & [ 1 , 2 ] , & [ 3 ] ] , & [ & mut [ 0 , 0 ] , & mut [ 0 ] ] )
333
+ . unwrap ( ) ;
334
+
335
+ assert_eq ! ( queue. available_desc( ) , 0 ) ;
336
+ assert ! ( !queue. can_pop( ) ) ;
337
+
338
+ let first_descriptor_index = queue. avail . ring [ 0 ] . read ( ) ;
339
+ assert_eq ! ( first_descriptor_index, token) ;
340
+ assert_eq ! ( queue. desc[ first_descriptor_index as usize ] . len. read( ) , 2 ) ;
341
+ assert_eq ! (
342
+ queue. desc[ first_descriptor_index as usize ] . flags. read( ) ,
343
+ DescFlags :: NEXT
344
+ ) ;
345
+ let second_descriptor_index = queue. desc [ first_descriptor_index as usize ] . next . read ( ) ;
346
+ assert_eq ! ( queue. desc[ second_descriptor_index as usize ] . len. read( ) , 1 ) ;
347
+ assert_eq ! (
348
+ queue. desc[ second_descriptor_index as usize ] . flags. read( ) ,
349
+ DescFlags :: NEXT
350
+ ) ;
351
+ let third_descriptor_index = queue. desc [ second_descriptor_index as usize ] . next . read ( ) ;
352
+ assert_eq ! ( queue. desc[ third_descriptor_index as usize ] . len. read( ) , 2 ) ;
353
+ assert_eq ! (
354
+ queue. desc[ third_descriptor_index as usize ] . flags. read( ) ,
355
+ DescFlags :: NEXT | DescFlags :: WRITE
356
+ ) ;
357
+ let fourth_descriptor_index = queue. desc [ third_descriptor_index as usize ] . next . read ( ) ;
358
+ assert_eq ! ( queue. desc[ fourth_descriptor_index as usize ] . len. read( ) , 1 ) ;
359
+ assert_eq ! (
360
+ queue. desc[ fourth_descriptor_index as usize ] . flags. read( ) ,
361
+ DescFlags :: WRITE
362
+ ) ;
363
+ }
364
+ }
0 commit comments