diff --git a/devices/src/virtio/snd/vios_backend/mod.rs b/devices/src/virtio/snd/vios_backend/mod.rs index 9184285f36..3176b39c69 100644 --- a/devices/src/virtio/snd/vios_backend/mod.rs +++ b/devices/src/virtio/snd/vios_backend/mod.rs @@ -75,7 +75,7 @@ pub type Result = std::result::Result; pub struct Sound { config: virtio_snd_config, virtio_features: u64, - worker_thread: Option>, + worker_thread: Option>>, vios_client: Arc, } @@ -141,15 +141,15 @@ impl VirtioDevice for Sound { Arc::new(Mutex::new(rx_queue)), ) { Ok(mut worker) => match worker.control_loop(kill_evt) { - Ok(_) => true, + Ok(_) => Ok(worker), Err(e) => { error!("virtio-snd: Error in worker loop: {}", e); - false + Err(anyhow!("virtio-snd: Error in worker loop: {}", e)) } }, Err(e) => { error!("virtio-snd: Failed to create worker: {}", e); - false + Err(anyhow!("virtio-snd: Failed to create worker: {}", e)) } }, )); @@ -162,7 +162,7 @@ impl VirtioDevice for Sound { if let Some(worker_thread) = self.worker_thread.take() { let worker_status = worker_thread.stop(); - ret = worker_status; + ret = worker_status.is_ok(); } if let Err(e) = self.vios_client.stop_bg_thread() { error!("virtio-snd: Failed to stop vios background thread: {}", e); @@ -170,6 +170,57 @@ impl VirtioDevice for Sound { } ret } + + fn virtio_sleep(&mut self) -> anyhow::Result>> { + if let Some(worker_thread) = self.worker_thread.take() { + let worker = worker_thread.stop(); + self.vios_client + .stop_bg_thread() + .context("failed to stop VioS Client background thread")?; + + let mut worker = worker.context("failed to stop worker_thread")?; + let ctrl_queue = worker.control_queue.clone(); + let event_queue = worker.event_queue.take().unwrap(); + let tx_queue = worker.tx_queue.clone(); + let rx_queue = worker.rx_queue.clone(); + + // Must drop worker to drop all references to queues. + // This also drops the io_thread + drop(worker); + + let ctrl_queue = match Arc::try_unwrap(ctrl_queue) { + Ok(q) => q.into_inner(), + Err(_) => panic!("too many refs to snd control queue"), + }; + let tx_queue = match Arc::try_unwrap(tx_queue) { + Ok(q) => q.into_inner(), + Err(_) => panic!("too many refs to snd tx queue"), + }; + let rx_queue = match Arc::try_unwrap(rx_queue) { + Ok(q) => q.into_inner(), + Err(_) => panic!("too many refs to snd rx queue"), + }; + let queues = vec![ctrl_queue, event_queue, tx_queue, rx_queue]; + return Ok(Some(BTreeMap::from_iter(queues.into_iter().enumerate()))); + } + Ok(None) + } + + fn virtio_wake( + &mut self, + device_state: Option<(GuestMemory, Interrupt, BTreeMap)>, + ) -> anyhow::Result<()> { + match device_state { + None => Ok(()), + Some((mem, interrupt, queues)) => { + // TODO: activate is just what we want at the moment, but we should probably move + // it into a "start workers" function to make it obvious that it isn't strictly + // used for activate events. + self.activate(mem, interrupt, queues)?; + Ok(()) + } + } + } } /// Creates a new virtio sound device connected to a VioS backend diff --git a/devices/src/virtio/snd/vios_backend/worker.rs b/devices/src/virtio/snd/vios_backend/worker.rs index 3b336ab8a7..d4db30354b 100644 --- a/devices/src/virtio/snd/vios_backend/worker.rs +++ b/devices/src/virtio/snd/vios_backend/worker.rs @@ -29,10 +29,12 @@ use crate::virtio::Queue; pub struct Worker { // Lock order: Must never hold more than one queue lock at the same time. interrupt: Interrupt, - control_queue: Arc>, - event_queue: Queue, + pub control_queue: Arc>, + pub event_queue: Option, vios_client: Arc, streams: Vec, + pub tx_queue: Arc>, + pub rx_queue: Arc>, io_thread: Option>>, io_kill: Event, } @@ -72,20 +74,30 @@ impl Worker { let interrupt_clone = interrupt.clone(); let senders: Vec>> = streams.iter().map(|sp| sp.msg_sender().clone()).collect(); + let tx_queue_thread = tx_queue.clone(); + let rx_queue_thread = rx_queue.clone(); let io_thread = thread::Builder::new() .name("v_snd_io".to_string()) .spawn(move || { try_set_real_time_priority(); - io_loop(interrupt_clone, tx_queue, rx_queue, senders, kill_io) + io_loop( + interrupt_clone, + tx_queue_thread, + rx_queue_thread, + senders, + kill_io, + ) }) .map_err(SoundError::CreateThread)?; Ok(Worker { interrupt, control_queue, - event_queue, + event_queue: Some(event_queue), vios_client, streams, + tx_queue, + rx_queue, io_thread: Some(io_thread), io_kill: self_kill_io, }) @@ -108,7 +120,10 @@ impl Worker { } let wait_ctx: WaitContext = WaitContext::build_with(&[ (self.control_queue.lock().event(), Token::ControlQAvailable), - (self.event_queue.event(), Token::EventQAvailable), + ( + self.event_queue.as_ref().expect("queue missing").event(), + Token::EventQAvailable, + ), (&event_notifier, Token::EventTriggered), (&kill_evt, Token::Kill), ]) @@ -119,6 +134,7 @@ impl Worker { .add(resample_evt, Token::InterruptResample) .map_err(SoundError::WaitCtx)?; } + let mut event_queue = self.event_queue.take().expect("event_queue missing"); 'wait: loop { let wait_events = wait_ctx.wait().map_err(SoundError::WaitCtx)?; @@ -136,14 +152,11 @@ impl Worker { // Just read from the event object to make sure the producer of such events // never blocks. The buffers will only be used when actual virtio-snd // events are triggered. - self.event_queue - .event() - .wait() - .map_err(SoundError::QueueEvt)?; + event_queue.event().wait().map_err(SoundError::QueueEvt)?; } Token::EventTriggered => { event_notifier.wait().map_err(SoundError::QueueEvt)?; - self.process_event_triggered()?; + self.process_event_triggered(&mut event_queue)?; } Token::InterruptResample => { self.interrupt.interrupt_resample(); @@ -155,6 +168,7 @@ impl Worker { } } } + self.event_queue = Some(event_queue); Ok(()) } @@ -352,14 +366,14 @@ impl Worker { Ok(()) } - fn process_event_triggered(&mut self) -> Result<()> { + fn process_event_triggered(&mut self, event_queue: &mut Queue) -> Result<()> { while let Some(evt) = self.vios_client.pop_event() { - if let Some(mut desc) = self.event_queue.pop() { + if let Some(mut desc) = event_queue.pop() { let writer = &mut desc.writer; writer.write_obj(evt).map_err(SoundError::QueueIO)?; let len = writer.bytes_written() as u32; - self.event_queue.add_used(desc, len); - self.event_queue.trigger_interrupt(&self.interrupt); + event_queue.add_used(desc, len); + event_queue.trigger_interrupt(&self.interrupt); } else { warn!("virtio-snd: Dropping event because there are no buffers in virtqueue"); }