@@ -467,42 +467,51 @@ impl VhostUserSoundThread {
467
467
return Ok ( true ) ;
468
468
}
469
469
470
+ // Instead of counting descriptor chain lengths, encode the "parsing" logic in
471
+ // an enumeration. Then, the compiler will complain about any unhandled
472
+ // match {} cases if any part of the code is changed. This makes invalid
473
+ // states unrepresentable in the source code.
470
474
#[ derive( Copy , Clone , PartialEq , Debug ) ]
471
- enum TxState {
475
+ enum IoState {
472
476
Ready ,
473
477
WaitingBufferForStreamId ( u32 ) ,
474
478
Done ,
475
479
}
476
480
481
+ // Keep log of stream IDs to wake up, in case the guest has queued more than
482
+ // one.
477
483
let mut stream_ids = BTreeSet :: default ( ) ;
478
484
479
485
for desc_chain in requests {
480
- let mut state = TxState :: Ready ;
486
+ let mut state = IoState :: Ready ;
481
487
let mut buffers = vec ! [ ] ;
482
488
let descriptors: Vec < _ > = desc_chain. clone ( ) . collect ( ) ;
483
489
let message = Arc :: new ( IOMessage {
484
490
vring : vring. clone ( ) ,
485
491
status : VIRTIO_SND_S_OK . into ( ) ,
486
492
latency_bytes : 0 . into ( ) ,
487
493
desc_chain : desc_chain. clone ( ) ,
488
- descriptor : descriptors. last ( ) . cloned ( ) . unwrap ( ) ,
494
+ response_descriptor : descriptors. last ( ) . cloned ( ) . ok_or_else ( || {
495
+ log:: error!( "Received IO request with an empty descriptor chain." ) ;
496
+ Error :: UnexpectedDescriptorCount ( 0 )
497
+ } ) ?,
489
498
} ) ;
490
499
for descriptor in & descriptors {
491
500
match state {
492
- TxState :: Done => {
501
+ IoState :: Done => {
493
502
return Err ( Error :: UnexpectedDescriptorCount ( descriptors. len ( ) ) . into ( ) ) ;
494
503
}
495
- TxState :: Ready if descriptor. is_write_only ( ) => {
504
+ IoState :: Ready if descriptor. is_write_only ( ) => {
496
505
if descriptor. len ( ) as usize != size_of :: < VirtioSoundPcmStatus > ( ) {
497
506
return Err ( Error :: UnexpectedDescriptorSize (
498
507
size_of :: < VirtioSoundPcmStatus > ( ) ,
499
508
descriptor. len ( ) ,
500
509
)
501
510
. into ( ) ) ;
502
511
}
503
- state = TxState :: Done ;
512
+ state = IoState :: Done ;
504
513
}
505
- TxState :: WaitingBufferForStreamId ( stream_id) if descriptor. is_write_only ( ) => {
514
+ IoState :: WaitingBufferForStreamId ( stream_id) if descriptor. is_write_only ( ) => {
506
515
if descriptor. len ( ) as usize != size_of :: < VirtioSoundPcmStatus > ( ) {
507
516
return Err ( Error :: UnexpectedDescriptorSize (
508
517
size_of :: < VirtioSoundPcmStatus > ( ) ,
@@ -514,9 +523,9 @@ impl VhostUserSoundThread {
514
523
for b in std:: mem:: take ( & mut buffers) {
515
524
streams[ stream_id as usize ] . buffers . push_back ( b) ;
516
525
}
517
- state = TxState :: Done ;
526
+ state = IoState :: Done ;
518
527
}
519
- TxState :: Ready
528
+ IoState :: Ready
520
529
if descriptor. len ( ) as usize != size_of :: < VirtioSoundPcmXfer > ( ) =>
521
530
{
522
531
return Err ( Error :: UnexpectedDescriptorSize (
@@ -525,17 +534,17 @@ impl VhostUserSoundThread {
525
534
)
526
535
. into ( ) ) ;
527
536
}
528
- TxState :: Ready => {
537
+ IoState :: Ready => {
529
538
let xfer = desc_chain
530
539
. memory ( )
531
540
. read_obj :: < VirtioSoundPcmXfer > ( descriptor. addr ( ) )
532
541
. map_err ( |_| Error :: DescriptorReadFailed ) ?;
533
542
let stream_id: u32 = xfer. stream_id . into ( ) ;
534
543
stream_ids. insert ( stream_id) ;
535
544
536
- state = TxState :: WaitingBufferForStreamId ( stream_id) ;
545
+ state = IoState :: WaitingBufferForStreamId ( stream_id) ;
537
546
}
538
- TxState :: WaitingBufferForStreamId ( stream_id)
547
+ IoState :: WaitingBufferForStreamId ( stream_id)
539
548
if descriptor. len ( ) as usize == size_of :: < VirtioSoundPcmXfer > ( ) =>
540
549
{
541
550
return Err ( Error :: UnexpectedDescriptorSize (
@@ -548,16 +557,16 @@ impl VhostUserSoundThread {
548
557
)
549
558
. into ( ) ) ;
550
559
}
551
- TxState :: WaitingBufferForStreamId ( _stream_id) => {
552
- /*
553
- Rather than copying the content of a descriptor, buffer keeps a pointer to it.
554
- When we copy just after the request is enqueued, the guest's userspace may or
555
- may not have updated the buffer contents. Guest driver simply moves buffers
556
- from the used ring to the available ring without knowing whether the content
557
- has been updated. The device only reads the buffer from guest memory when the
558
- audio engine requires it, which is about after a period thus ensuring that the
559
- buffer is up-to-date.
560
- */
560
+ IoState :: WaitingBufferForStreamId ( _stream_id) => {
561
+ // In the case of TX/Playback:
562
+ //
563
+ // Rather than copying the content of a descriptor, buffer keeps a pointer
564
+ // to it. When we copy just after the request is enqueued, the guest's
565
+ // userspace may or may not have updated the buffer contents. Guest driver
566
+ // simply moves buffers from the used ring to the available ring without
567
+ // knowing whether the content has been updated. The device only reads the
568
+ // buffer from guest memory when the audio engine requires it, which is
569
+ // about after a period thus ensuring that the buffer is up-to-date.
561
570
buffers. push ( Buffer :: new ( * descriptor, Arc :: clone ( & message) ) ) ;
562
571
}
563
572
}
@@ -626,7 +635,7 @@ impl VhostUserSoundBackend {
626
635
} ,
627
636
] ;
628
637
let chmaps: Arc < RwLock < Vec < VirtioSoundChmapInfo > > > = Arc :: new ( RwLock :: new ( chmaps_info) ) ;
629
- log:: trace!( "VhostUserSoundBackend::new config {:?}" , & config) ;
638
+ log:: trace!( "VhostUserSoundBackend::new( config = {:?}) " , & config) ;
630
639
let threads = if config. multi_thread {
631
640
vec ! [
632
641
RwLock :: new( VhostUserSoundThread :: new(
@@ -691,7 +700,10 @@ impl VhostUserBackend<VringRwLock, ()> for VhostUserSoundBackend {
691
700
}
692
701
693
702
fn max_queue_size ( & self ) -> usize {
694
- // TODO: Investigate if an alternative value makes any difference.
703
+ // The linux kernel driver does no checks for queue length and fails silently if
704
+ // a queue is filled up. In this case, adding an element to the queue
705
+ // returns ENOSPC and the element is not queued for a later attempt and
706
+ // is lost. `64` is a "good enough" value from our observations.
695
707
64
696
708
}
697
709
0 commit comments