Skip to content

Commit 30d5cf4

Browse files
epilysstsquad
authored andcommitted
sound: clean up and improve comments, definitions
- Add some documentation comments - Rename TxState to IoState in preparation for RX implementation - Fix inline rust code not being escaped properly in doc comments - Rename some fields to make their function clearer - Cleanup some error log messages - Replace an old TODO comment about queue size with an explanation Signed-off-by: Manos Pitsidianakis <[email protected]>
1 parent 04f80fc commit 30d5cf4

File tree

3 files changed

+62
-44
lines changed

3 files changed

+62
-44
lines changed

staging/vhost-device-sound/src/audio_backends/alsa.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,15 +195,14 @@ fn update_pcm(
195195
Ok(())
196196
}
197197

198-
// Returns `true` if the function should be called again, because there are are
199-
// more data left to write.
198+
// Returns `Ok(true)` if the function should be called again, because there are
199+
// are more data left to write.
200200
fn write_samples_direct(
201201
pcm: &alsa::PCM,
202202
stream: &mut Stream,
203203
mmap: &mut alsa::direct::pcm::MmapPlayback<u8>,
204204
) -> AResult<bool> {
205205
while mmap.avail() > 0 {
206-
// Write samples to DMA area from iterator
207206
let Some(buffer) = stream.buffers.front_mut() else {
208207
return Ok(false);
209208
};
@@ -223,6 +222,7 @@ fn write_samples_direct(
223222
}
224223
Ok(v) => v,
225224
};
225+
// Write samples to DMA area from iterator
226226
let mut iter = buf[0..read_bytes as usize].iter().cloned();
227227
let frames = mmap.write(&mut iter);
228228
let written_bytes = pcm.frames_to_bytes(frames);
@@ -240,6 +240,8 @@ fn write_samples_direct(
240240
}
241241
}
242242

243+
// Returns `Ok(true)` if the function should be called again, because there are
244+
// are more data left to write.
243245
fn write_samples_io(
244246
p: &alsa::PCM,
245247
streams: &Arc<RwLock<Vec<Stream>>>,
@@ -385,6 +387,9 @@ impl AlsaBackend {
385387
let mut senders = Vec::with_capacity(streams_no);
386388
for i in 0..streams_no {
387389
let (sender, receiver) = channel();
390+
391+
// Initialize with a dummy value, which will be updated every time we call
392+
// `update_pcm`.
388393
let pcm = Arc::new(Mutex::new(PCM::new("default", Direction::Playback, false)?));
389394

390395
let mtx = Arc::clone(&pcm);
@@ -495,16 +500,18 @@ impl AlsaBackend {
495500
msg.code = VIRTIO_SND_S_BAD_MSG;
496501
continue;
497502
};
498-
// Stop the worker.
503+
// Stop worker thread
499504
senders[stream_id].send(false).unwrap();
500505
let mut streams = streams.write().unwrap();
501506
if let Err(err) = streams[stream_id].state.release() {
502507
log::error!("Stream {}: {}", stream_id, err);
503508
msg.code = VIRTIO_SND_S_BAD_MSG;
504509
}
505-
// Release buffers even if state transition is invalid. If it is invalid, we
506-
// won't be in a valid device state anyway so better to get rid of them and
507-
// free the virt queue.
510+
// Drop pending stream buffers to complete pending I/O messages
511+
//
512+
// This will release buffers even if state transition is invalid. If it is
513+
// invalid, we won't be in a valid device state anyway so better to get rid of
514+
// them and free the virt queue.
508515
std::mem::take(&mut streams[stream_id].buffers);
509516
}
510517
AlsaAction::SetParameters(stream_id, mut msg) => {
@@ -545,9 +552,9 @@ impl AlsaBackend {
545552
st.params.format = request.format;
546553
st.params.rate = request.rate;
547554
}
555+
// Manually drop msg for faster response: the kernel has a timeout.
556+
drop(msg);
548557
}
549-
// Manually drop msg for faster response: the kernel has a timeout.
550-
drop(msg);
551558
update_pcm(&pcms[stream_id], stream_id, &streams)?;
552559
}
553560
}

staging/vhost-device-sound/src/device.rs

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -467,42 +467,51 @@ impl VhostUserSoundThread {
467467
return Ok(true);
468468
}
469469

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.
470474
#[derive(Copy, Clone, PartialEq, Debug)]
471-
enum TxState {
475+
enum IoState {
472476
Ready,
473477
WaitingBufferForStreamId(u32),
474478
Done,
475479
}
476480

481+
// Keep log of stream IDs to wake up, in case the guest has queued more than
482+
// one.
477483
let mut stream_ids = BTreeSet::default();
478484

479485
for desc_chain in requests {
480-
let mut state = TxState::Ready;
486+
let mut state = IoState::Ready;
481487
let mut buffers = vec![];
482488
let descriptors: Vec<_> = desc_chain.clone().collect();
483489
let message = Arc::new(IOMessage {
484490
vring: vring.clone(),
485491
status: VIRTIO_SND_S_OK.into(),
486492
latency_bytes: 0.into(),
487493
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+
})?,
489498
});
490499
for descriptor in &descriptors {
491500
match state {
492-
TxState::Done => {
501+
IoState::Done => {
493502
return Err(Error::UnexpectedDescriptorCount(descriptors.len()).into());
494503
}
495-
TxState::Ready if descriptor.is_write_only() => {
504+
IoState::Ready if descriptor.is_write_only() => {
496505
if descriptor.len() as usize != size_of::<VirtioSoundPcmStatus>() {
497506
return Err(Error::UnexpectedDescriptorSize(
498507
size_of::<VirtioSoundPcmStatus>(),
499508
descriptor.len(),
500509
)
501510
.into());
502511
}
503-
state = TxState::Done;
512+
state = IoState::Done;
504513
}
505-
TxState::WaitingBufferForStreamId(stream_id) if descriptor.is_write_only() => {
514+
IoState::WaitingBufferForStreamId(stream_id) if descriptor.is_write_only() => {
506515
if descriptor.len() as usize != size_of::<VirtioSoundPcmStatus>() {
507516
return Err(Error::UnexpectedDescriptorSize(
508517
size_of::<VirtioSoundPcmStatus>(),
@@ -514,9 +523,9 @@ impl VhostUserSoundThread {
514523
for b in std::mem::take(&mut buffers) {
515524
streams[stream_id as usize].buffers.push_back(b);
516525
}
517-
state = TxState::Done;
526+
state = IoState::Done;
518527
}
519-
TxState::Ready
528+
IoState::Ready
520529
if descriptor.len() as usize != size_of::<VirtioSoundPcmXfer>() =>
521530
{
522531
return Err(Error::UnexpectedDescriptorSize(
@@ -525,17 +534,17 @@ impl VhostUserSoundThread {
525534
)
526535
.into());
527536
}
528-
TxState::Ready => {
537+
IoState::Ready => {
529538
let xfer = desc_chain
530539
.memory()
531540
.read_obj::<VirtioSoundPcmXfer>(descriptor.addr())
532541
.map_err(|_| Error::DescriptorReadFailed)?;
533542
let stream_id: u32 = xfer.stream_id.into();
534543
stream_ids.insert(stream_id);
535544

536-
state = TxState::WaitingBufferForStreamId(stream_id);
545+
state = IoState::WaitingBufferForStreamId(stream_id);
537546
}
538-
TxState::WaitingBufferForStreamId(stream_id)
547+
IoState::WaitingBufferForStreamId(stream_id)
539548
if descriptor.len() as usize == size_of::<VirtioSoundPcmXfer>() =>
540549
{
541550
return Err(Error::UnexpectedDescriptorSize(
@@ -548,16 +557,16 @@ impl VhostUserSoundThread {
548557
)
549558
.into());
550559
}
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.
561570
buffers.push(Buffer::new(*descriptor, Arc::clone(&message)));
562571
}
563572
}
@@ -626,7 +635,7 @@ impl VhostUserSoundBackend {
626635
},
627636
];
628637
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);
630639
let threads = if config.multi_thread {
631640
vec![
632641
RwLock::new(VhostUserSoundThread::new(
@@ -691,7 +700,10 @@ impl VhostUserBackend<VringRwLock, ()> for VhostUserSoundBackend {
691700
}
692701

693702
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.
695707
64
696708
}
697709

staging/vhost-device-sound/src/lib.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -250,46 +250,45 @@ pub struct IOMessage {
250250
status: std::sync::atomic::AtomicU32,
251251
pub latency_bytes: std::sync::atomic::AtomicU32,
252252
desc_chain: SoundDescriptorChain,
253-
descriptor: virtio_queue::Descriptor,
253+
response_descriptor: virtio_queue::Descriptor,
254254
vring: VringRwLock,
255255
}
256256

257257
impl Drop for IOMessage {
258258
fn drop(&mut self) {
259-
log::trace!("dropping IOMessage");
260259
let resp = VirtioSoundPcmStatus {
261260
status: self.status.load(std::sync::atomic::Ordering::SeqCst).into(),
262261
latency_bytes: self
263262
.latency_bytes
264263
.load(std::sync::atomic::Ordering::SeqCst)
265264
.into(),
266265
};
266+
log::trace!("dropping IOMessage {:?}", resp);
267267

268268
if let Err(err) = self
269269
.desc_chain
270270
.memory()
271-
.write_obj(resp, self.descriptor.addr())
271+
.write_obj(resp, self.response_descriptor.addr())
272272
{
273273
log::error!("Error::DescriptorWriteFailed: {}", err);
274274
return;
275275
}
276-
if self
276+
if let Err(err) = self
277277
.vring
278278
.add_used(self.desc_chain.head_index(), resp.as_slice().len() as u32)
279-
.is_err()
280279
{
281-
log::error!("Couldn't add used");
280+
log::error!("Couldn't add used bytes count to vring: {}", err);
282281
}
283-
if self.vring.signal_used_queue().is_err() {
284-
log::error!("Couldn't signal used queue");
282+
if let Err(err) = self.vring.signal_used_queue() {
283+
log::error!("Couldn't signal used queue: {}", err);
285284
}
286285
}
287286
}
288287

289288
/// This is the public API through which an external program starts the
290289
/// vhost-device-sound backend server.
291290
pub fn start_backend_server(config: SoundConfig) {
292-
log::trace!("Using config {:?}", &config);
291+
log::trace!("Using config {:?}.", &config);
293292
let listener = Listener::new(config.get_socket_path(), true).unwrap();
294293
let backend = Arc::new(VhostUserSoundBackend::new(config).unwrap());
295294

@@ -300,12 +299,12 @@ pub fn start_backend_server(config: SoundConfig) {
300299
)
301300
.unwrap();
302301

303-
log::trace!("Starting daemon");
302+
log::trace!("Starting daemon.");
304303
daemon.start(listener).unwrap();
305304

306305
match daemon.wait() {
307306
Ok(()) => {
308-
info!("Stopping cleanly");
307+
info!("Stopping cleanly.");
309308
}
310309
Err(vhost_user_backend::Error::HandleRequest(vhost_user::Error::PartialMessage)) => {
311310
info!(

0 commit comments

Comments
 (0)