@@ -9,6 +9,8 @@ use std::cmp::min;
9
9
use std:: num:: Wrapping ;
10
10
use std:: sync:: atomic:: { fence, Ordering } ;
11
11
12
+ use utils:: usize_to_u64;
13
+
12
14
use crate :: logger:: error;
13
15
use crate :: vstate:: memory:: {
14
16
Address , ByteValued , Bytes , GuestAddress , GuestMemory , GuestMemoryMmap ,
@@ -31,12 +33,15 @@ pub(super) const FIRECRACKER_MAX_QUEUE_SIZE: u16 = 256;
31
33
#[ derive( Debug , thiserror:: Error , displaydoc:: Display ) ]
32
34
pub enum QueueError {
33
35
/// Descriptor index out of bounds: {0}.
34
- DescIndexOutOfBounds ( u16 ) ,
36
+ DescIndexOutOfBounds ( u32 ) ,
35
37
/// Failed to write value into the virtio queue used ring: {0}
36
38
UsedRing ( #[ from] vm_memory:: GuestMemoryError ) ,
37
39
}
38
40
39
41
/// A virtio descriptor constraints with C representative.
42
+ /// Taken from Virtio spec:
43
+ /// https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-430008
44
+ /// 2.6.5 The Virtqueue Descriptor Table
40
45
#[ repr( C ) ]
41
46
#[ derive( Default , Clone , Copy ) ]
42
47
struct Descriptor {
@@ -49,6 +54,20 @@ struct Descriptor {
49
54
// SAFETY: `Descriptor` is a POD and contains no padding.
50
55
unsafe impl ByteValued for Descriptor { }
51
56
57
+ /// A virtio used element in the used ring.
58
+ /// Taken from Virtio spec:
59
+ /// https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-430008
60
+ /// 2.6.8 The Virtqueue Used Ring
61
+ #[ repr( C ) ]
62
+ #[ derive( Default , Clone , Copy ) ]
63
+ struct UsedElement {
64
+ id : u32 ,
65
+ len : u32 ,
66
+ }
67
+
68
+ // SAFETY: `UsedElement` is a POD and contains no padding.
69
+ unsafe impl ByteValued for UsedElement { }
70
+
52
71
/// A virtio descriptor chain.
53
72
#[ derive( Debug ) ]
54
73
pub struct DescriptorChain < ' a , M : GuestMemory = GuestMemoryMmap > {
@@ -430,31 +449,51 @@ impl Queue {
430
449
) -> Result < ( ) , QueueError > {
431
450
debug_assert ! ( self . is_layout_valid( mem) ) ;
432
451
433
- if desc_index >= self . actual_size ( ) {
434
- error ! (
435
- "attempted to add out of bounds descriptor to used ring: {}" ,
436
- desc_index
437
- ) ;
438
- return Err ( QueueError :: DescIndexOutOfBounds ( desc_index) ) ;
439
- }
440
-
441
- let used_ring = self . used_ring ;
442
- let next_used = u64:: from ( self . next_used . 0 % self . actual_size ( ) ) ;
443
- let used_elem = used_ring. unchecked_add ( 4 + next_used * 8 ) ;
444
-
445
- mem. write_obj ( u32:: from ( desc_index) , used_elem) ?;
446
-
447
- let len_addr = used_elem. unchecked_add ( 4 ) ;
448
- mem. write_obj ( len, len_addr) ?;
452
+ let next_used = self . next_used . 0 % self . actual_size ( ) ;
453
+ let used_element = UsedElement {
454
+ id : u32:: from ( desc_index) ,
455
+ len,
456
+ } ;
457
+ self . write_used_ring ( mem, next_used, used_element) ?;
449
458
450
459
self . num_added += Wrapping ( 1 ) ;
451
460
self . next_used += Wrapping ( 1 ) ;
452
461
453
462
// This fence ensures all descriptor writes are visible before the index update is.
454
463
fence ( Ordering :: Release ) ;
455
464
456
- let next_used_addr = used_ring. unchecked_add ( 2 ) ;
457
- mem. write_obj ( self . next_used . 0 , next_used_addr)
465
+ self . set_next_used ( self . next_used . 0 , mem) ;
466
+ Ok ( ( ) )
467
+ }
468
+
469
+ fn write_used_ring < M : GuestMemory > (
470
+ & self ,
471
+ mem : & M ,
472
+ index : u16 ,
473
+ used_element : UsedElement ,
474
+ ) -> Result < ( ) , QueueError > {
475
+ if used_element. id >= u32:: from ( self . actual_size ( ) ) {
476
+ error ! (
477
+ "attempted to add out of bounds descriptor to used ring: {}" ,
478
+ used_element. id
479
+ ) ;
480
+ return Err ( QueueError :: DescIndexOutOfBounds ( used_element. id ) ) ;
481
+ }
482
+
483
+ // Used ring has layout:
484
+ // struct UsedRing {
485
+ // flags: u16,
486
+ // idx: u16,
487
+ // ring: [UsedElement; <queue size>],
488
+ // avail_event: u16,
489
+ // }
490
+ // We calculate offset into `ring` field.
491
+ let used_ring_offset = std:: mem:: size_of :: < u16 > ( )
492
+ + std:: mem:: size_of :: < u16 > ( )
493
+ + std:: mem:: size_of :: < UsedElement > ( ) * usize:: from ( index) ;
494
+ let used_element_address = self . used_ring . unchecked_add ( usize_to_u64 ( used_ring_offset) ) ;
495
+
496
+ mem. write_obj ( used_element, used_element_address)
458
497
. map_err ( QueueError :: UsedRing )
459
498
}
460
499
@@ -484,15 +523,45 @@ impl Queue {
484
523
Wrapping ( mem. read_obj :: < u16 > ( used_event_addr) . unwrap ( ) )
485
524
}
486
525
487
- /// Helper method that writes `val` to the `avail_event` field of the used ring.
488
- fn set_avail_event < M : GuestMemory > ( & mut self , val : u16 , mem : & M ) {
526
+ /// Helper method that writes to the `avail_event` field of the used ring.
527
+ #[ inline( always) ]
528
+ fn set_avail_event < M : GuestMemory > ( & mut self , avail_event : u16 , mem : & M ) {
489
529
debug_assert ! ( self . is_layout_valid( mem) ) ;
490
530
531
+ // Used ring has layout:
532
+ // struct UsedRing {
533
+ // flags: u16,
534
+ // idx: u16,
535
+ // ring: [UsedElement; <queue size>],
536
+ // avail_event: u16,
537
+ // }
538
+ // We calculate offset into `avail_event` field.
539
+ let avail_event_offset = std:: mem:: size_of :: < u16 > ( )
540
+ + std:: mem:: size_of :: < u16 > ( )
541
+ + std:: mem:: size_of :: < UsedElement > ( ) * usize:: from ( self . actual_size ( ) ) ;
491
542
let avail_event_addr = self
492
543
. used_ring
493
- . unchecked_add ( u64:: from ( 4 + 8 * self . actual_size ( ) ) ) ;
544
+ . unchecked_add ( usize_to_u64 ( avail_event_offset) ) ;
545
+
546
+ mem. write_obj ( avail_event, avail_event_addr) . unwrap ( ) ;
547
+ }
494
548
495
- mem. write_obj ( val, avail_event_addr) . unwrap ( ) ;
549
+ /// Helper method that writes to the `idx` field of the used ring.
550
+ #[ inline( always) ]
551
+ fn set_next_used < M : GuestMemory > ( & mut self , next_used : u16 , mem : & M ) {
552
+ debug_assert ! ( self . is_layout_valid( mem) ) ;
553
+
554
+ // Used ring has layout:
555
+ // struct UsedRing {
556
+ // flags: u16,
557
+ // idx: u16,
558
+ // ring: [UsedElement; <queue size>],
559
+ // avail_event: u16,
560
+ // }
561
+ // We calculate offset into `idx` field.
562
+ let idx_offset = std:: mem:: size_of :: < u16 > ( ) ;
563
+ let next_used_addr = self . used_ring . unchecked_add ( usize_to_u64 ( idx_offset) ) ;
564
+ mem. write_obj ( next_used, next_used_addr) . unwrap ( ) ;
496
565
}
497
566
498
567
/// Try to enable notification events from the guest driver. Returns true if notifications were
0 commit comments