devices: virtio-snd: add sleep/wake to vios_backend

Add sleep/wake to the vios_backend Sound device.

BUG=b:297091430
TEST=presubmit

Change-Id: I10d54dcf3ab15f86e1e96a9e25375f83120cc5ef
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4808782
Reviewed-by: Frederick Mayle <fmayle@google.com>
Reviewed-by: Noah Gold <nkgold@google.com>
Commit-Queue: Elie Kheirallah <khei@google.com>
This commit is contained in:
Elie Kheirallah 2023-07-18 15:32:09 +00:00 committed by crosvm LUCI
parent a6c50e4128
commit 8879591538
2 changed files with 84 additions and 19 deletions

View file

@ -75,7 +75,7 @@ pub type Result<T> = std::result::Result<T, SoundError>;
pub struct Sound {
config: virtio_snd_config,
virtio_features: u64,
worker_thread: Option<WorkerThread<bool>>,
worker_thread: Option<WorkerThread<anyhow::Result<Worker>>>,
vios_client: Arc<VioSClient>,
}
@ -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<Option<BTreeMap<usize, Queue>>> {
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<usize, Queue>)>,
) -> 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

View file

@ -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<Mutex<Queue>>,
event_queue: Queue,
pub control_queue: Arc<Mutex<Queue>>,
pub event_queue: Option<Queue>,
vios_client: Arc<VioSClient>,
streams: Vec<StreamProxy>,
pub tx_queue: Arc<Mutex<Queue>>,
pub rx_queue: Arc<Mutex<Queue>>,
io_thread: Option<thread::JoinHandle<Result<()>>>,
io_kill: Event,
}
@ -72,20 +74,30 @@ impl Worker {
let interrupt_clone = interrupt.clone();
let senders: Vec<Sender<Box<StreamMsg>>> =
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<Token> = 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");
}