mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-25 05:03:05 +00:00
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:
parent
a6c50e4128
commit
8879591538
2 changed files with 84 additions and 19 deletions
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue