@@ -9,6 +9,8 @@ use std::cmp::min;
99use std:: num:: Wrapping ;
1010use std:: sync:: atomic:: { fence, Ordering } ;
1111
12+ use utils:: usize_to_u64;
13+
1214use crate :: logger:: error;
1315use crate :: vstate:: memory:: {
1416 Address , ByteValued , Bytes , GuestAddress , GuestMemory , GuestMemoryMmap ,
@@ -31,12 +33,15 @@ pub(super) const FIRECRACKER_MAX_QUEUE_SIZE: u16 = 256;
3133#[ derive( Debug , thiserror:: Error , displaydoc:: Display ) ]
3234pub enum QueueError {
3335 /// Descriptor index out of bounds: {0}.
34- DescIndexOutOfBounds ( u16 ) ,
36+ DescIndexOutOfBounds ( u32 ) ,
3537 /// Failed to write value into the virtio queue used ring: {0}
3638 UsedRing ( #[ from] vm_memory:: GuestMemoryError ) ,
3739}
3840
3941/// 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
4045#[ repr( C ) ]
4146#[ derive( Default , Clone , Copy ) ]
4247struct Descriptor {
@@ -49,6 +54,20 @@ struct Descriptor {
4954// SAFETY: `Descriptor` is a POD and contains no padding.
5055unsafe impl ByteValued for Descriptor { }
5156
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+
5271/// A virtio descriptor chain.
5372#[ derive( Debug ) ]
5473pub struct DescriptorChain < ' a , M : GuestMemory = GuestMemoryMmap > {
@@ -430,31 +449,51 @@ impl Queue {
430449 ) -> Result < ( ) , QueueError > {
431450 debug_assert ! ( self . is_layout_valid( mem) ) ;
432451
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) ?;
449458
450459 self . num_added += Wrapping ( 1 ) ;
451460 self . next_used += Wrapping ( 1 ) ;
452461
453462 // This fence ensures all descriptor writes are visible before the index update is.
454463 fence ( Ordering :: Release ) ;
455464
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)
458497 . map_err ( QueueError :: UsedRing )
459498 }
460499
@@ -484,15 +523,45 @@ impl Queue {
484523 Wrapping ( mem. read_obj :: < u16 > ( used_event_addr) . unwrap ( ) )
485524 }
486525
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 ) {
489529 debug_assert ! ( self . is_layout_valid( mem) ) ;
490530
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 ( ) ) ;
491542 let avail_event_addr = self
492543 . 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+ }
494548
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 ( ) ;
496565 }
497566
498567 /// Try to enable notification events from the guest driver. Returns true if notifications were
0 commit comments