* [PATCH v5 01/10] hw/audio/virtio-sound: remove channel positions field from VirtIOSoundPCMStream
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 02/10] hw/audio/virtio-sound: drop unused struct VirtIOSoundPCMStream.flushing field Alexander Mikhalitsyn
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
This field seems to be useless currently, as we
don't implement VIRTIO_SND_R_CHMAP_INFO.
Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
v4:
- moved this patch to the beginning of the series
(suggested by Manos Pitsidianakis)
v3:
- this patch was added
---
hw/audio/virtio-snd.c | 2 --
include/hw/audio/virtio-snd.h | 2 --
2 files changed, 4 deletions(-)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index 694bcebb60f..46d81cc9ec4 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -463,8 +463,6 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
stream->info.rates = supported_rates;
stream->params = *params;
- stream->positions[0] = VIRTIO_SND_CHMAP_FL;
- stream->positions[1] = VIRTIO_SND_CHMAP_FR;
stream->as = as;
if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h
index e28f1be5db9..f8bb5c95b97 100644
--- a/include/hw/audio/virtio-snd.h
+++ b/include/hw/audio/virtio-snd.h
@@ -137,8 +137,6 @@ struct VirtIOSoundPCMStream {
virtio_snd_pcm_info info;
virtio_snd_pcm_set_params params;
uint32_t id;
- /* channel position values (VIRTIO_SND_CHMAP_XXX) */
- uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
VirtIOSound *s;
bool flushing;
audsettings as;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 02/10] hw/audio/virtio-sound: drop unused struct VirtIOSoundPCMStream.flushing field
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 01/10] hw/audio/virtio-sound: remove channel positions field from VirtIOSoundPCMStream Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 03/10] hw/audio/virtio-sound: remove command and stream mutexes Alexander Mikhalitsyn
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
v5:
- added RWB tag from Manos
v4:
- this commit was split from
"hw/audio/virtio-sound: add stream state variable"
(suggested by Manos Pitsidianakis)
---
include/hw/audio/virtio-snd.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h
index f8bb5c95b97..b60b098ccd8 100644
--- a/include/hw/audio/virtio-snd.h
+++ b/include/hw/audio/virtio-snd.h
@@ -138,7 +138,6 @@ struct VirtIOSoundPCMStream {
virtio_snd_pcm_set_params params;
uint32_t id;
VirtIOSound *s;
- bool flushing;
audsettings as;
union {
SWVoiceIn *in;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 03/10] hw/audio/virtio-sound: remove command and stream mutexes
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 01/10] hw/audio/virtio-sound: remove channel positions field from VirtIOSoundPCMStream Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 02/10] hw/audio/virtio-sound: drop unused struct VirtIOSoundPCMStream.flushing field Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 04/10] hw/audio/virtio-sound: allocate an array of streams Alexander Mikhalitsyn
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Volker Rümelin <vr_qemu@t-online.de>
All code in virtio-snd.c runs with the BQL held. Remove the
command queue mutex and the stream queue mutexes. The qatomic
functions are also not needed.
Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
[AM: there were too many conflicts, I did `git checkout --ours -- <.>`
and then reimplemented the patch idea
/AM]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
hw/audio/virtio-snd.c | 246 +++++++++++++++-------------------
include/hw/audio/virtio-snd.h | 3 -
2 files changed, 110 insertions(+), 139 deletions(-)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index 46d81cc9ec4..cbf739f8a7d 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -19,7 +19,6 @@
#include "qemu/iov.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
-#include "qemu/lockable.h"
#include "system/runstate.h"
#include "trace.h"
#include "qapi/error.h"
@@ -442,7 +441,6 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
stream->id = stream_id;
stream->s = s;
stream->latency_bytes = 0;
- qemu_mutex_init(&stream->queue_mutex);
QSIMPLEQ_INIT(&stream->queue);
/*
@@ -566,9 +564,7 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
stream = virtio_snd_pcm_get_stream(s, stream_id);
if (stream) {
- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
- stream->active = start;
- }
+ stream->active = start;
if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
audio_be_set_active_out(s->audio_be, stream->voice.out, start);
} else {
@@ -592,10 +588,8 @@ static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream)
VirtIOSoundPCMBuffer *buffer, *next;
size_t count = 0;
- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
- QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
- count += 1;
- }
+ QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
+ count += 1;
}
return count;
}
@@ -737,23 +731,15 @@ static void virtio_snd_process_cmdq(VirtIOSound *s)
{
virtio_snd_ctrl_command *cmd;
- if (unlikely(qatomic_read(&s->processing_cmdq))) {
- return;
- }
-
- WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) {
- qatomic_set(&s->processing_cmdq, true);
- while (!QTAILQ_EMPTY(&s->cmdq)) {
- cmd = QTAILQ_FIRST(&s->cmdq);
+ while (!QTAILQ_EMPTY(&s->cmdq)) {
+ cmd = QTAILQ_FIRST(&s->cmdq);
- /* process command */
- process_cmd(s, cmd);
+ /* process command */
+ process_cmd(s, cmd);
- QTAILQ_REMOVE(&s->cmdq, cmd, next);
+ QTAILQ_REMOVE(&s->cmdq, cmd, next);
- virtio_snd_ctrl_cmd_free(cmd);
- }
- qatomic_set(&s->processing_cmdq, false);
+ virtio_snd_ctrl_cmd_free(cmd);
}
}
@@ -896,17 +882,16 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
if (!g_size_checked_add(&tmp, sizeof(VirtIOSoundPCMBuffer), size)) {
goto tx_err;
}
- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
- buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
- buffer->elem = elem;
- buffer->populated = false;
- buffer->vq = vq;
- buffer->size = size;
- buffer->offset = 0;
- stream->latency_bytes += size;
-
- QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
- }
+
+ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
+ buffer->elem = elem;
+ buffer->populated = false;
+ buffer->vq = vq;
+ buffer->size = size;
+ buffer->offset = 0;
+ stream->latency_bytes += size;
+
+ QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
continue;
tx_err:
@@ -983,14 +968,14 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
if (!g_size_checked_add(&tmp, sizeof(VirtIOSoundPCMBuffer), size)) {
goto rx_err;
}
- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
- buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
- buffer->elem = elem;
- buffer->vq = vq;
- buffer->size = 0;
- buffer->offset = 0;
- QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
- }
+
+ buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
+ buffer->elem = elem;
+ buffer->vq = vq;
+ buffer->size = 0;
+ buffer->offset = 0;
+ QSIMPLEQ_INSERT_TAIL(&stream->queue, buffer, entry);
+
continue;
rx_err:
@@ -1094,7 +1079,6 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
virtio_add_queue(vdev, 64, virtio_snd_handle_tx_xfer);
vsnd->queues[VIRTIO_SND_VQ_RX] =
virtio_add_queue(vdev, 64, virtio_snd_handle_rx_xfer);
- qemu_mutex_init(&vsnd->cmdq_mutex);
QTAILQ_INIT(&vsnd->cmdq);
QSIMPLEQ_INIT(&vsnd->invalid);
@@ -1162,52 +1146,50 @@ static void virtio_snd_pcm_out_cb(void *data, int available)
VirtIOSoundPCMBuffer *buffer;
size_t size;
- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
- while (!QSIMPLEQ_EMPTY(&stream->queue)) {
- buffer = QSIMPLEQ_FIRST(&stream->queue);
- if (!virtio_queue_ready(buffer->vq)) {
- return;
+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
+ buffer = QSIMPLEQ_FIRST(&stream->queue);
+ if (!virtio_queue_ready(buffer->vq)) {
+ return;
+ }
+ if (!stream->active) {
+ /* Stream has stopped, so do not perform audio_be_write. */
+ return_tx_buffer(stream, buffer);
+ continue;
+ }
+ if (!buffer->populated) {
+ iov_to_buf(buffer->elem->out_sg,
+ buffer->elem->out_num,
+ sizeof(virtio_snd_pcm_xfer),
+ buffer->data,
+ buffer->size);
+ buffer->populated = true;
+ }
+ for (;;) {
+ size = audio_be_write(stream->s->audio_be,
+ stream->voice.out,
+ buffer->data + buffer->offset,
+ MIN(buffer->size, available));
+ assert(size <= MIN(buffer->size, available));
+ if (size == 0) {
+ /* break out of both loops */
+ available = 0;
+ break;
}
- if (!stream->active) {
- /* Stream has stopped, so do not perform audio_be_write. */
+ buffer->size -= size;
+ buffer->offset += size;
+ available -= size;
+ update_latency(stream, size);
+ if (buffer->size < 1) {
return_tx_buffer(stream, buffer);
- continue;
- }
- if (!buffer->populated) {
- iov_to_buf(buffer->elem->out_sg,
- buffer->elem->out_num,
- sizeof(virtio_snd_pcm_xfer),
- buffer->data,
- buffer->size);
- buffer->populated = true;
- }
- for (;;) {
- size = audio_be_write(stream->s->audio_be,
- stream->voice.out,
- buffer->data + buffer->offset,
- MIN(buffer->size, available));
- assert(size <= MIN(buffer->size, available));
- if (size == 0) {
- /* break out of both loops */
- available = 0;
- break;
- }
- buffer->size -= size;
- buffer->offset += size;
- available -= size;
- update_latency(stream, size);
- if (buffer->size < 1) {
- return_tx_buffer(stream, buffer);
- break;
- }
- if (!available) {
- break;
- }
+ break;
}
if (!available) {
break;
}
}
+ if (!available) {
+ break;
+ }
}
}
@@ -1258,55 +1240,53 @@ static void virtio_snd_pcm_in_cb(void *data, int available)
VirtIOSoundPCMBuffer *buffer;
size_t size, max_size, to_read;
- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
- while (!QSIMPLEQ_EMPTY(&stream->queue)) {
- buffer = QSIMPLEQ_FIRST(&stream->queue);
- if (!virtio_queue_ready(buffer->vq)) {
- return;
- }
- if (!stream->active) {
- /* Stream has stopped, so do not perform audio_be_read. */
- return_rx_buffer(stream, buffer);
- continue;
- }
+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
+ buffer = QSIMPLEQ_FIRST(&stream->queue);
+ if (!virtio_queue_ready(buffer->vq)) {
+ return;
+ }
+ if (!stream->active) {
+ /* Stream has stopped, so do not perform audio_be_read. */
+ return_rx_buffer(stream, buffer);
+ continue;
+ }
- max_size = iov_size(buffer->elem->in_sg, buffer->elem->in_num);
- if (max_size <= sizeof(virtio_snd_pcm_status)) {
+ max_size = iov_size(buffer->elem->in_sg, buffer->elem->in_num);
+ if (max_size <= sizeof(virtio_snd_pcm_status)) {
+ return_rx_buffer(stream, buffer);
+ continue;
+ }
+ max_size -= sizeof(virtio_snd_pcm_status);
+
+ for (;;) {
+ if (buffer->size >= max_size) {
return_rx_buffer(stream, buffer);
- continue;
+ break;
}
- max_size -= sizeof(virtio_snd_pcm_status);
-
- for (;;) {
- if (buffer->size >= max_size) {
- return_rx_buffer(stream, buffer);
- break;
- }
- to_read = stream->params.period_bytes - buffer->size;
- to_read = MIN(to_read, available);
- to_read = MIN(to_read, max_size - buffer->size);
- size = audio_be_read(stream->s->audio_be,
- stream->voice.in,
- buffer->data + buffer->size,
- to_read);
- if (!size) {
- available = 0;
- break;
- }
- buffer->size += size;
- available -= size;
- if (buffer->size >= stream->params.period_bytes) {
- return_rx_buffer(stream, buffer);
- break;
- }
- if (!available) {
- break;
- }
+ to_read = stream->params.period_bytes - buffer->size;
+ to_read = MIN(to_read, available);
+ to_read = MIN(to_read, max_size - buffer->size);
+ size = audio_be_read(stream->s->audio_be,
+ stream->voice.in,
+ buffer->data + buffer->size,
+ to_read);
+ if (!size) {
+ available = 0;
+ break;
+ }
+ buffer->size += size;
+ available -= size;
+ if (buffer->size >= stream->params.period_bytes) {
+ return_rx_buffer(stream, buffer);
+ break;
}
if (!available) {
break;
}
}
+ if (!available) {
+ break;
+ }
}
}
@@ -1323,11 +1303,9 @@ static inline void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream)
(stream->info.direction == VIRTIO_SND_D_OUTPUT) ? return_tx_buffer :
return_rx_buffer;
- WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
- while (!QSIMPLEQ_EMPTY(&stream->queue)) {
- buffer = QSIMPLEQ_FIRST(&stream->queue);
- cb(stream, buffer);
- }
+ while (!QSIMPLEQ_EMPTY(&stream->queue)) {
+ buffer = QSIMPLEQ_FIRST(&stream->queue);
+ cb(stream, buffer);
}
}
@@ -1346,14 +1324,12 @@ static void virtio_snd_unrealize(DeviceState *dev)
if (stream) {
virtio_snd_process_cmdq(stream->s);
virtio_snd_pcm_close(stream);
- qemu_mutex_destroy(&stream->queue_mutex);
g_free(stream);
}
}
g_free(vsnd->pcm.streams);
}
g_free(vsnd->pcm.pcm_params);
- qemu_mutex_destroy(&vsnd->cmdq_mutex);
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
@@ -1374,12 +1350,10 @@ static void virtio_snd_reset(VirtIODevice *vdev)
*/
g_assert(QSIMPLEQ_EMPTY(&vsnd->invalid));
- WITH_QEMU_LOCK_GUARD(&vsnd->cmdq_mutex) {
- while (!QTAILQ_EMPTY(&vsnd->cmdq)) {
- cmd = QTAILQ_FIRST(&vsnd->cmdq);
- QTAILQ_REMOVE(&vsnd->cmdq, cmd, next);
- virtio_snd_ctrl_cmd_free(cmd);
- }
+ while (!QTAILQ_EMPTY(&vsnd->cmdq)) {
+ cmd = QTAILQ_FIRST(&vsnd->cmdq);
+ QTAILQ_REMOVE(&vsnd->cmdq, cmd, next);
+ virtio_snd_ctrl_cmd_free(cmd);
}
}
diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h
index b60b098ccd8..884c475ffd9 100644
--- a/include/hw/audio/virtio-snd.h
+++ b/include/hw/audio/virtio-snd.h
@@ -143,7 +143,6 @@ struct VirtIOSoundPCMStream {
SWVoiceIn *in;
SWVoiceOut *out;
} voice;
- QemuMutex queue_mutex;
bool active;
uint32_t latency_bytes;
QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue;
@@ -215,9 +214,7 @@ struct VirtIOSound {
AudioBackend *audio_be;
VMChangeStateEntry *vmstate;
virtio_snd_config snd_conf;
- QemuMutex cmdq_mutex;
QTAILQ_HEAD(, virtio_snd_ctrl_command) cmdq;
- bool processing_cmdq;
/*
* Convenience queue to keep track of invalid tx/rx queue messages inside
* the tx/rx callbacks.
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 04/10] hw/audio/virtio-sound: allocate an array of streams
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
` (2 preceding siblings ...)
2026-08-03 8:11 ` [PATCH v5 03/10] hw/audio/virtio-sound: remove command and stream mutexes Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 05/10] hw/audio/virtio-sound: free all stream buffers on reset Alexander Mikhalitsyn
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Volker Rümelin <vr_qemu@t-online.de>
It is much easier to migrate an array of structs than individual
structs that are accessed via a pointer to a pointer to an array
of pointers to struct.
For this reason, allocate an array of streams in
virtio_snd_realize() and initialise all stream variables that
are constant at runtime immediately after allocation.
This makes it easier to remove the virtio_snd_set_pcm_params()
and virtio_snd_pcm_prepare() calls in the realisation phase and
to migrate the audio streams of the virtio sound device after
the next few patches.
Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
[AM: there were too many conflicts, I did `git checkout --ours -- <.>`
and then reimplemented the patch idea
/AM]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
v4:
- fixed stream->info.channels_max value
(problem was noticed by Manos Pitsidianakis)
v3:
As suggested by Marc-André Lureau:
- drop VirtIOSoundPCMStream's id field
---
hw/audio/virtio-snd.c | 33 +++++++++++++++++++++------------
include/hw/audio/virtio-snd.h | 2 +-
2 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index cbf739f8a7d..84083be8825 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -436,12 +436,9 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
stream = virtio_snd_pcm_get_stream(s, stream_id);
if (stream == NULL) {
- stream = g_new0(VirtIOSoundPCMStream, 1);
+ stream = &s->streams[stream_id];
stream->active = false;
- stream->id = stream_id;
- stream->s = s;
stream->latency_bytes = 0;
- QSIMPLEQ_INIT(&stream->queue);
/*
* stream_id >= s->snd_conf.streams was checked before so this is
@@ -451,14 +448,7 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
}
virtio_snd_get_qemu_audsettings(&as, params);
- stream->info.direction = stream_id < s->snd_conf.streams / 2 +
- (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT;
- stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID;
- stream->info.features = 0;
- stream->info.channels_min = 1;
stream->info.channels_max = as.nchannels;
- stream->info.formats = supported_formats;
- stream->info.rates = supported_rates;
stream->params = *params;
stream->as = as;
@@ -1056,6 +1046,24 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
vsnd->vmstate =
qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
+ vsnd->streams = g_new0(VirtIOSoundPCMStream, vsnd->snd_conf.streams);
+
+ for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
+ VirtIOSoundPCMStream *stream = &vsnd->streams[i];
+
+ stream->s = vsnd;
+ QSIMPLEQ_INIT(&stream->queue);
+ stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID;
+ stream->info.features = 0;
+ stream->info.formats = supported_formats;
+ stream->info.rates = supported_rates;
+ stream->info.direction =
+ i < vsnd->snd_conf.streams / 2 + (vsnd->snd_conf.streams & 1)
+ ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT;
+ stream->info.channels_min = 1;
+ stream->info.channels_max = 2;
+ }
+
vsnd->pcm.streams =
g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
vsnd->pcm.pcm_params =
@@ -1324,12 +1332,13 @@ static void virtio_snd_unrealize(DeviceState *dev)
if (stream) {
virtio_snd_process_cmdq(stream->s);
virtio_snd_pcm_close(stream);
- g_free(stream);
}
}
g_free(vsnd->pcm.streams);
}
g_free(vsnd->pcm.pcm_params);
+ g_free(vsnd->streams);
+ vsnd->streams = NULL;
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h
index 884c475ffd9..97ffaae18be 100644
--- a/include/hw/audio/virtio-snd.h
+++ b/include/hw/audio/virtio-snd.h
@@ -136,7 +136,6 @@ struct VirtIOSoundPCM {
struct VirtIOSoundPCMStream {
virtio_snd_pcm_info info;
virtio_snd_pcm_set_params params;
- uint32_t id;
VirtIOSound *s;
audsettings as;
union {
@@ -211,6 +210,7 @@ struct VirtIOSound {
VirtQueue *queues[VIRTIO_SND_VQ_MAX];
uint64_t features;
VirtIOSoundPCM pcm;
+ VirtIOSoundPCMStream *streams;
AudioBackend *audio_be;
VMChangeStateEntry *vmstate;
virtio_snd_config snd_conf;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 05/10] hw/audio/virtio-sound: free all stream buffers on reset
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
` (3 preceding siblings ...)
2026-08-03 8:11 ` [PATCH v5 04/10] hw/audio/virtio-sound: allocate an array of streams Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 06/10] hw/audio/virtio-sound: split out virtio_snd_pcm_start_stop() Alexander Mikhalitsyn
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Volker Rümelin <vr_qemu@t-online.de>
All remaining stream buffers in the stream queues must
be freed after a reset. This is the initial state of the
virtio-sound device.
Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
[AM: trivial variable rename s -> vsnd]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
v4:
- reduced a scope of loop counter variable in virtio_snd_reset
(as suggested by Manos Pitsidianakis)
---
hw/audio/virtio-snd.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index 84083be8825..f9822fc609a 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -1364,6 +1364,16 @@ static void virtio_snd_reset(VirtIODevice *vdev)
QTAILQ_REMOVE(&vsnd->cmdq, cmd, next);
virtio_snd_ctrl_cmd_free(cmd);
}
+
+ for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
+ VirtIOSoundPCMStream *stream = &vsnd->streams[i];
+ VirtIOSoundPCMBuffer *buffer;
+
+ while ((buffer = QSIMPLEQ_FIRST(&stream->queue))) {
+ QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry);
+ virtio_snd_pcm_buffer_free(buffer);
+ }
+ }
}
static void virtio_snd_class_init(ObjectClass *klass, const void *data)
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 06/10] hw/audio/virtio-sound: split out virtio_snd_pcm_start_stop()
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
` (4 preceding siblings ...)
2026-08-03 8:11 ` [PATCH v5 05/10] hw/audio/virtio-sound: free all stream buffers on reset Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 07/10] hw/audio/virtio-sound: add stream state variable Alexander Mikhalitsyn
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Volker Rümelin <vr_qemu@t-online.de>
Split out virtio_snd_pcm_start_stop(). This is a preparation
for the next patch so that it doesn't become too big.
Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
[AM: there were too many conflicts, I did `git checkout --ours -- <.>`
and then reimplemented the patch idea
/AM]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
v4:
- minor formatting change in qemu_log_mask()
- moved the trace_*() calls at the start of the function
(as suggested by Manos Pitsidianakis)
v3:
- resurrected error_report(..) as suggested by Marc-André Lureau
---
hw/audio/virtio-snd.c | 58 ++++++++++++++++++++++++++++---------------
1 file changed, 38 insertions(+), 20 deletions(-)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index f9822fc609a..46026b8cffc 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -520,7 +520,43 @@ static void virtio_snd_handle_pcm_prepare(VirtIOSound *s,
}
/*
- * Handles VIRTIO_SND_R_PCM_START.
+ * Starts/Stops a VirtIOSound card stream.
+ * Returns the response status code. (VIRTIO_SND_S_*).
+ *
+ * @s: VirtIOSound device
+ * @stream_id: stream id
+ * @start: whether to start or stop the stream
+ */
+static uint32_t virtio_snd_pcm_start_stop(VirtIOSound *s,
+ uint32_t stream_id,
+ bool start)
+{
+ VirtIOSoundPCMStream *stream;
+
+ trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
+ "VIRTIO_SND_R_PCM_STOP",
+ stream_id);
+
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
+ if (!stream) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Invalid stream id: %"PRIu32 "\n",
+ __func__, stream_id);
+ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ }
+
+ stream->active = start;
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
+ audio_be_set_active_out(s->audio_be, stream->voice.out, start);
+ } else {
+ audio_be_set_active_in(s->audio_be, stream->voice.in, start);
+ }
+
+ return cpu_to_le32(VIRTIO_SND_S_OK);
+}
+
+/*
+ * Handles VIRTIO_SND_R_PCM_START and VIRTIO_SND_R_PCM_STOP.
*
* @s: VirtIOSound device
* @cmd: The request command queue element from VirtIOSound cmdq field
@@ -530,7 +566,6 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
virtio_snd_ctrl_command *cmd,
bool start)
{
- VirtIOSoundPCMStream *stream;
virtio_snd_pcm_hdr req;
uint32_t stream_id;
size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
@@ -548,24 +583,7 @@ static void virtio_snd_handle_pcm_start_stop(VirtIOSound *s,
}
stream_id = le32_to_cpu(req.stream_id);
- cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
- trace_virtio_snd_handle_pcm_start_stop(start ? "VIRTIO_SND_R_PCM_START" :
- "VIRTIO_SND_R_PCM_STOP", stream_id);
-
- stream = virtio_snd_pcm_get_stream(s, stream_id);
- if (stream) {
- stream->active = start;
- if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
- audio_be_set_active_out(s->audio_be, stream->voice.out, start);
- } else {
- audio_be_set_active_in(s->audio_be, stream->voice.in, start);
- }
- } else {
- error_report("Invalid stream id: %"PRIu32, stream_id);
- cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
- return;
- }
- stream->active = start;
+ cmd->resp.code = virtio_snd_pcm_start_stop(s, stream_id, start);
}
/*
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 07/10] hw/audio/virtio-sound: add stream state variable
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
` (5 preceding siblings ...)
2026-08-03 8:11 ` [PATCH v5 06/10] hw/audio/virtio-sound: split out virtio_snd_pcm_start_stop() Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 08/10] hw/audio/virtio-sound: introduce virtio_snd_pcm_open() Alexander Mikhalitsyn
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Volker Rümelin <vr_qemu@t-online.de>
So far, only rudimentary checks have been made to ensure that
the guest only performs state transitions described in
virtio-v1.2-csd01 5.14.6.6.1 PCM Command Lifecycle. While this
is not a Device Requirement, let's add a state variable
per audio stream and check all state transitions.
Because only permitted state transitions are now possible, only one
copy of the audio stream parameters is required and these do not
need to be initialised with default values.
The state variable will also make it easier to restore the audio
stream after migration.
Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
[AM: there were too many conflicts, I did `git checkout --ours -- <.>`
and then reimplemented the patch idea
/AM]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
---
v5:
- checkpatch fix
v4:
- switched to enum virtio_snd_pcm_state
(suggested by Manos Pitsidianakis)
- call virtio_snd_pcm_close() when switching to
VSND_PCMSTREAM_STATE_PARAMS_SET and VSND_PCMSTREAM_STATE_RELEASED
states
- added virtio_error() in case when stream->state is unexpected
- fixed an error message text in virtio_snd_set_pcm_params
(suggestions from Marc-André Lureau)
v3:
- explicitly call virtio_snd_pcm_close() from unrealize
- remove a call to virtio_snd_pcm_flush() from virtio_snd_pcm_close()
[ ^ this was my rebase mistake ]
---
hw/audio/virtio-snd.c | 213 ++++++++++++++++++++--------------
include/hw/audio/virtio-snd.h | 17 +--
2 files changed, 124 insertions(+), 106 deletions(-)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index 46026b8cffc..1c218569cfd 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -30,11 +30,26 @@
#define VIRTIO_SOUND_CHMAP_DEFAULT 0
#define VIRTIO_SOUND_HDA_FN_NID 0
+typedef enum virtio_snd_pcm_state {
+ VIRTIO_SND_PCM_STATE_UNINIT = 0,
+ VIRTIO_SND_PCM_STATE_PARAMS_SET,
+ VIRTIO_SND_PCM_STATE_PREPARED,
+ VIRTIO_SND_PCM_STATE_STARTED,
+ VIRTIO_SND_PCM_STATE_STOPPED,
+ VIRTIO_SND_PCM_STATE_RELEASED,
+} virtio_snd_pcm_state;
+
+static inline bool virtio_snd_pcm_state_prepared(virtio_snd_pcm_state s)
+{
+ return s > VIRTIO_SND_PCM_STATE_PARAMS_SET &&
+ s < VIRTIO_SND_PCM_STATE_RELEASED;
+}
+
static void virtio_snd_pcm_out_cb(void *data, int available);
static void virtio_snd_process_cmdq(VirtIOSound *s);
static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream);
+static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream);
static void virtio_snd_pcm_in_cb(void *data, int available);
-static void virtio_snd_unrealize(DeviceState *dev);
static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
| BIT(VIRTIO_SND_PCM_FMT_U8)
@@ -129,7 +144,7 @@ static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s,
uint32_t stream_id)
{
return stream_id >= s->snd_conf.streams ? NULL :
- s->pcm.streams[stream_id];
+ &s->streams[stream_id];
}
/*
@@ -141,8 +156,8 @@ static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s,
static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
uint32_t stream_id)
{
- return stream_id >= s->snd_conf.streams ? NULL
- : &s->pcm.pcm_params[stream_id];
+ return stream_id >= s->snd_conf.streams ? NULL :
+ &s->streams[stream_id].params;
}
/*
@@ -245,11 +260,10 @@ static void virtio_snd_handle_pcm_info(VirtIOSound *s,
/*
* Set the given stream params.
- * Called by both virtio_snd_handle_pcm_set_params and during device
- * initialization.
* Returns the response status code. (VIRTIO_SND_S_*).
*
* @s: VirtIOSound device
+ * @stream_id: stream id
* @params: The PCM params as defined in the virtio specification
*/
static
@@ -257,14 +271,27 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
uint32_t stream_id,
virtio_snd_pcm_set_params *params)
{
+ VirtIOSoundPCMStream *stream;
virtio_snd_pcm_set_params *st_params;
- if (stream_id >= s->snd_conf.streams || s->pcm.pcm_params == NULL) {
- virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n");
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
+ if (!stream) {
+ virtio_error(VIRTIO_DEVICE(s), "invalid stream id: %"PRIu32,
+ stream_id);
return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
}
- st_params = virtio_snd_pcm_get_params(s, stream_id);
+ switch (stream->state) {
+ case VIRTIO_SND_PCM_STATE_UNINIT:
+ case VIRTIO_SND_PCM_STATE_PARAMS_SET:
+ case VIRTIO_SND_PCM_STATE_PREPARED:
+ case VIRTIO_SND_PCM_STATE_RELEASED:
+ break;
+ default:
+ virtio_error(VIRTIO_DEVICE(s), "unexpected stream state: %"PRIu32,
+ stream->state);
+ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ }
if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) {
error_report("Number of channels is not supported.");
@@ -281,6 +308,8 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
}
+ st_params = virtio_snd_pcm_get_params(s, stream_id);
+
st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes);
st_params->period_bytes = le32_to_cpu(params->period_bytes);
st_params->features = le32_to_cpu(params->features);
@@ -289,6 +318,15 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
st_params->format = params->format;
st_params->rate = params->rate;
+ if (virtio_snd_pcm_state_prepared(stream->state)) {
+ /* implicit VIRTIO_SND_R_PCM_RELEASE */
+ virtio_snd_pcm_flush(stream);
+ }
+
+ virtio_snd_pcm_close(stream);
+
+ stream->state = VIRTIO_SND_PCM_STATE_PARAMS_SET;
+
return cpu_to_le32(VIRTIO_SND_S_OK);
}
@@ -398,15 +436,12 @@ static void virtio_snd_get_qemu_audsettings(audsettings *as,
*/
static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
{
- if (stream) {
- virtio_snd_pcm_flush(stream);
- if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
- audio_be_close_out(stream->s->audio_be, stream->voice.out);
- stream->voice.out = NULL;
- } else if (stream->info.direction == VIRTIO_SND_D_INPUT) {
- audio_be_close_in(stream->s->audio_be, stream->voice.in);
- stream->voice.in = NULL;
- }
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
+ audio_be_close_out(stream->s->audio_be, stream->voice.out);
+ stream->voice.out = NULL;
+ } else if (stream->info.direction == VIRTIO_SND_D_INPUT) {
+ audio_be_close_in(stream->s->audio_be, stream->voice.in);
+ stream->voice.in = NULL;
}
}
@@ -423,33 +458,26 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
virtio_snd_pcm_set_params *params;
VirtIOSoundPCMStream *stream;
- if (s->pcm.streams == NULL ||
- s->pcm.pcm_params == NULL ||
- stream_id >= s->snd_conf.streams) {
+ stream = virtio_snd_pcm_get_stream(s, stream_id);
+ if (!stream) {
return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
}
- params = virtio_snd_pcm_get_params(s, stream_id);
- if (params == NULL) {
+ switch (stream->state) {
+ case VIRTIO_SND_PCM_STATE_PARAMS_SET:
+ case VIRTIO_SND_PCM_STATE_PREPARED:
+ case VIRTIO_SND_PCM_STATE_RELEASED:
+ break;
+ default:
+ virtio_error(VIRTIO_DEVICE(s), "unexpected stream state: %"PRIu32,
+ stream->state);
return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
}
- stream = virtio_snd_pcm_get_stream(s, stream_id);
- if (stream == NULL) {
- stream = &s->streams[stream_id];
- stream->active = false;
- stream->latency_bytes = 0;
-
- /*
- * stream_id >= s->snd_conf.streams was checked before so this is
- * in-bounds
- */
- s->pcm.streams[stream_id] = stream;
- }
+ params = virtio_snd_pcm_get_params(s, stream_id);
virtio_snd_get_qemu_audsettings(&as, params);
stream->info.channels_max = as.nchannels;
- stream->params = *params;
stream->as = as;
@@ -471,6 +499,8 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
audio_be_set_volume_in_lr(s->audio_be, stream->voice.in, 0, 255, 255);
}
+ stream->state = VIRTIO_SND_PCM_STATE_PREPARED;
+
return cpu_to_le32(VIRTIO_SND_S_OK);
}
@@ -545,7 +575,31 @@ static uint32_t virtio_snd_pcm_start_stop(VirtIOSound *s,
return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
}
- stream->active = start;
+ if (start) {
+ switch (stream->state) {
+ case VIRTIO_SND_PCM_STATE_PREPARED:
+ case VIRTIO_SND_PCM_STATE_STOPPED:
+ break;
+ default:
+ virtio_error(VIRTIO_DEVICE(s), "unexpected stream state: %"PRIu32,
+ stream->state);
+ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ }
+
+ stream->state = VIRTIO_SND_PCM_STATE_STARTED;
+ } else {
+ switch (stream->state) {
+ case VIRTIO_SND_PCM_STATE_STARTED:
+ break;
+ default:
+ virtio_error(VIRTIO_DEVICE(s), "unexpected stream state: %"PRIu32,
+ stream->state);
+ return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ }
+
+ stream->state = VIRTIO_SND_PCM_STATE_STOPPED;
+ }
+
if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
audio_be_set_active_out(s->audio_be, stream->voice.out, start);
} else {
@@ -639,6 +693,17 @@ static void virtio_snd_handle_pcm_release(VirtIOSound *s,
return;
}
+ switch (stream->state) {
+ case VIRTIO_SND_PCM_STATE_PREPARED:
+ case VIRTIO_SND_PCM_STATE_STOPPED:
+ break;
+ default:
+ virtio_error(VIRTIO_DEVICE(s), "unexpected stream state: %"PRIu32,
+ stream->state);
+ cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
+ return;
+ }
+
if (virtio_snd_pcm_get_io_msgs_count(stream)) {
/*
* virtio-v1.2-csd01, 5.14.6.6.5.1,
@@ -653,6 +718,10 @@ static void virtio_snd_handle_pcm_release(VirtIOSound *s,
virtio_snd_pcm_flush(stream);
}
+ virtio_snd_pcm_close(stream);
+
+ stream->state = VIRTIO_SND_PCM_STATE_RELEASED;
+
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
}
@@ -876,12 +945,11 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
size = iov_size(elem->out_sg, elem->out_num) - msg_sz;
stream_id = le32_to_cpu(hdr.stream_id);
- if (stream_id >= vsnd->snd_conf.streams
- || vsnd->pcm.streams[stream_id] == NULL) {
+ if (stream_id >= vsnd->snd_conf.streams) {
goto tx_err;
}
- stream = vsnd->pcm.streams[stream_id];
+ stream = &vsnd->streams[stream_id];
if (stream->info.direction != VIRTIO_SND_D_OUTPUT) {
goto tx_err;
}
@@ -959,15 +1027,13 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
}
stream_id = le32_to_cpu(hdr.stream_id);
- if (stream_id >= vsnd->snd_conf.streams
- || !vsnd->pcm.streams[stream_id]) {
+ if (stream_id >= vsnd->snd_conf.streams) {
goto rx_err;
}
- stream = vsnd->pcm.streams[stream_id];
+ stream = &vsnd->streams[stream_id];
size = iov_size(elem->in_sg, elem->in_num);
- if (stream == NULL
- || stream->info.direction != VIRTIO_SND_D_INPUT
+ if (stream->info.direction != VIRTIO_SND_D_INPUT
|| size < sizeof(virtio_snd_pcm_status)) {
goto rx_err;
}
@@ -1031,8 +1097,6 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
ERRP_GUARD();
VirtIOSound *vsnd = VIRTIO_SND(dev);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
- virtio_snd_pcm_set_params default_params = { 0 };
- uint32_t status;
trace_virtio_snd_realize(vsnd);
@@ -1069,6 +1133,7 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
VirtIOSoundPCMStream *stream = &vsnd->streams[i];
+ stream->state = VIRTIO_SND_PCM_STATE_UNINIT;
stream->s = vsnd;
QSIMPLEQ_INIT(&stream->queue);
stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID;
@@ -1082,21 +1147,9 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
stream->info.channels_max = 2;
}
- vsnd->pcm.streams =
- g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
- vsnd->pcm.pcm_params =
- g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams);
-
virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
- /* set default params for all streams */
- default_params.features = 0;
- default_params.buffer_bytes = cpu_to_le32(8192);
- default_params.period_bytes = cpu_to_le32(2048);
- default_params.channels = 2;
- default_params.format = VIRTIO_SND_PCM_FMT_S16;
- default_params.rate = VIRTIO_SND_PCM_RATE_48000;
vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl);
vsnd->queues[VIRTIO_SND_VQ_EVENT] =
@@ -1107,28 +1160,6 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
virtio_add_queue(vdev, 64, virtio_snd_handle_rx_xfer);
QTAILQ_INIT(&vsnd->cmdq);
QSIMPLEQ_INIT(&vsnd->invalid);
-
- for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
- status = virtio_snd_set_pcm_params(vsnd, i, &default_params);
- if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
- error_setg(errp,
- "Can't initialize stream params, device responded with %s.",
- print_code(status));
- goto error_cleanup;
- }
- status = virtio_snd_pcm_prepare(vsnd, i);
- if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
- error_setg(errp,
- "Can't prepare streams, device responded with %s.",
- print_code(status));
- goto error_cleanup;
- }
- }
-
- return;
-
-error_cleanup:
- virtio_snd_unrealize(dev);
}
static inline void update_latency(VirtIOSoundPCMStream *s, size_t used)
@@ -1177,7 +1208,7 @@ static void virtio_snd_pcm_out_cb(void *data, int available)
if (!virtio_queue_ready(buffer->vq)) {
return;
}
- if (!stream->active) {
+ if (stream->state != VIRTIO_SND_PCM_STATE_STARTED) {
/* Stream has stopped, so do not perform audio_be_write. */
return_tx_buffer(stream, buffer);
continue;
@@ -1271,7 +1302,7 @@ static void virtio_snd_pcm_in_cb(void *data, int available)
if (!virtio_queue_ready(buffer->vq)) {
return;
}
- if (!stream->active) {
+ if (stream->state != VIRTIO_SND_PCM_STATE_STARTED) {
/* Stream has stopped, so do not perform audio_be_read. */
return_rx_buffer(stream, buffer);
continue;
@@ -1344,17 +1375,16 @@ static void virtio_snd_unrealize(DeviceState *dev)
qemu_del_vm_change_state_handler(vsnd->vmstate);
trace_virtio_snd_unrealize(vsnd);
- if (vsnd->pcm.streams) {
+ if (vsnd->streams) {
+ virtio_snd_process_cmdq(vsnd);
for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
- stream = vsnd->pcm.streams[i];
- if (stream) {
- virtio_snd_process_cmdq(stream->s);
- virtio_snd_pcm_close(stream);
+ stream = &vsnd->streams[i];
+ if (virtio_snd_pcm_state_prepared(stream->state)) {
+ virtio_snd_pcm_flush(stream);
}
+ virtio_snd_pcm_close(stream);
}
- g_free(vsnd->pcm.streams);
}
- g_free(vsnd->pcm.pcm_params);
g_free(vsnd->streams);
vsnd->streams = NULL;
virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
@@ -1387,6 +1417,9 @@ static void virtio_snd_reset(VirtIODevice *vdev)
VirtIOSoundPCMStream *stream = &vsnd->streams[i];
VirtIOSoundPCMBuffer *buffer;
+ virtio_snd_pcm_close(stream);
+ stream->state = VIRTIO_SND_PCM_STATE_UNINIT;
+
while ((buffer = QSIMPLEQ_FIRST(&stream->queue))) {
QSIMPLEQ_REMOVE_HEAD(&stream->queue, entry);
virtio_snd_pcm_buffer_free(buffer);
diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h
index 97ffaae18be..85d5d7c8619 100644
--- a/include/hw/audio/virtio-snd.h
+++ b/include/hw/audio/virtio-snd.h
@@ -75,8 +75,6 @@ typedef struct VirtIOSoundPCMStream VirtIOSoundPCMStream;
typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command;
-typedef struct VirtIOSoundPCM VirtIOSoundPCM;
-
typedef struct VirtIOSoundPCMBuffer VirtIOSoundPCMBuffer;
/*
@@ -121,28 +119,16 @@ struct VirtIOSoundPCMBuffer {
uint8_t data[];
};
-struct VirtIOSoundPCM {
- /*
- * PCM parameters are a separate field instead of a VirtIOSoundPCMStream
- * field, because the operation of PCM control requests is first
- * VIRTIO_SND_R_PCM_SET_PARAMS and then VIRTIO_SND_R_PCM_PREPARE; this
- * means that some times we get parameters without having an allocated
- * stream yet.
- */
- virtio_snd_pcm_set_params *pcm_params;
- VirtIOSoundPCMStream **streams;
-};
-
struct VirtIOSoundPCMStream {
virtio_snd_pcm_info info;
virtio_snd_pcm_set_params params;
+ uint32_t state;
VirtIOSound *s;
audsettings as;
union {
SWVoiceIn *in;
SWVoiceOut *out;
} voice;
- bool active;
uint32_t latency_bytes;
QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue;
};
@@ -209,7 +195,6 @@ struct VirtIOSound {
VirtQueue *queues[VIRTIO_SND_VQ_MAX];
uint64_t features;
- VirtIOSoundPCM pcm;
VirtIOSoundPCMStream *streams;
AudioBackend *audio_be;
VMChangeStateEntry *vmstate;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 08/10] hw/audio/virtio-sound: introduce virtio_snd_pcm_open()
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
` (6 preceding siblings ...)
2026-08-03 8:11 ` [PATCH v5 07/10] hw/audio/virtio-sound: add stream state variable Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 09/10] hw/audio/virtio-sound: introduce virtio_snd_set_active() Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 10/10] hw/audio/virtio-sound: add missing vmstate fields Alexander Mikhalitsyn
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Volker Rümelin <vr_qemu@t-online.de>
Split out the function virtio_snd_pcm_open() from
virtio_snd_pcm_prepare(). A later patch also needs
the new function. There is no functional change.
Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
[AM: trivial rebase changes]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
hw/audio/virtio-snd.c | 58 ++++++++++++++++++++++++-------------------
1 file changed, 32 insertions(+), 26 deletions(-)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index 1c218569cfd..7944d2b3b95 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -429,6 +429,37 @@ static void virtio_snd_get_qemu_audsettings(audsettings *as,
as->big_endian = false; /* Conforming to VIRTIO 1.0: always little endian. */
}
+/*
+ * Open a stream.
+ *
+ * @stream: VirtIOSoundPCMStream *stream
+ */
+static void virtio_snd_pcm_open(VirtIOSoundPCMStream *stream)
+{
+ virtio_snd_get_qemu_audsettings(&stream->as, &stream->params);
+ stream->info.channels_max = stream->as.nchannels;
+
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
+ stream->voice.out = audio_be_open_out(stream->s->audio_be,
+ stream->voice.out,
+ "virtio-sound.out",
+ stream,
+ virtio_snd_pcm_out_cb,
+ &stream->as);
+ audio_be_set_volume_out_lr(stream->s->audio_be,
+ stream->voice.out, 0, 255, 255);
+ } else {
+ stream->voice.in = audio_be_open_in(stream->s->audio_be,
+ stream->voice.in,
+ "virtio-sound.in",
+ stream,
+ virtio_snd_pcm_in_cb,
+ &stream->as);
+ audio_be_set_volume_in_lr(stream->s->audio_be,
+ stream->voice.in, 0, 255, 255);
+ }
+}
+
/*
* Close a stream and free all its resources.
*
@@ -454,8 +485,6 @@ static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
*/
static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
{
- audsettings as;
- virtio_snd_pcm_set_params *params;
VirtIOSoundPCMStream *stream;
stream = virtio_snd_pcm_get_stream(s, stream_id);
@@ -474,30 +503,7 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
}
- params = virtio_snd_pcm_get_params(s, stream_id);
-
- virtio_snd_get_qemu_audsettings(&as, params);
- stream->info.channels_max = as.nchannels;
-
- stream->as = as;
-
- if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
- stream->voice.out = audio_be_open_out(s->audio_be,
- stream->voice.out,
- "virtio-sound.out",
- stream,
- virtio_snd_pcm_out_cb,
- &as);
- audio_be_set_volume_out_lr(s->audio_be, stream->voice.out, 0, 255, 255);
- } else {
- stream->voice.in = audio_be_open_in(s->audio_be,
- stream->voice.in,
- "virtio-sound.in",
- stream,
- virtio_snd_pcm_in_cb,
- &as);
- audio_be_set_volume_in_lr(s->audio_be, stream->voice.in, 0, 255, 255);
- }
+ virtio_snd_pcm_open(stream);
stream->state = VIRTIO_SND_PCM_STATE_PREPARED;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 09/10] hw/audio/virtio-sound: introduce virtio_snd_set_active()
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
` (7 preceding siblings ...)
2026-08-03 8:11 ` [PATCH v5 08/10] hw/audio/virtio-sound: introduce virtio_snd_pcm_open() Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
2026-08-03 8:11 ` [PATCH v5 10/10] hw/audio/virtio-sound: add missing vmstate fields Alexander Mikhalitsyn
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Volker Rümelin <vr_qemu@t-online.de>
Split out the function virtio_snd_pcm_set_active() from
virtio_snd_pcm_start_stop(). A later patch also needs this
new funcion. There is no functional change.
Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
[AM: trivial rebase changes]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/audio/virtio-snd.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index 7944d2b3b95..12473862d24 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -460,6 +460,21 @@ static void virtio_snd_pcm_open(VirtIOSoundPCMStream *stream)
}
}
+/*
+ * Activate/deactivate a stream.
+ *
+ * @stream: VirtIOSoundPCMStream *stream
+ * @active: whether to activate or deactivate the stream
+ */
+static void virtio_snd_pcm_set_active(VirtIOSoundPCMStream *stream, bool active)
+{
+ if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
+ audio_be_set_active_out(stream->s->audio_be, stream->voice.out, active);
+ } else {
+ audio_be_set_active_in(stream->s->audio_be, stream->voice.in, active);
+ }
+}
+
/*
* Close a stream and free all its resources.
*
@@ -606,11 +621,7 @@ static uint32_t virtio_snd_pcm_start_stop(VirtIOSound *s,
stream->state = VIRTIO_SND_PCM_STATE_STOPPED;
}
- if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
- audio_be_set_active_out(s->audio_be, stream->voice.out, start);
- } else {
- audio_be_set_active_in(s->audio_be, stream->voice.in, start);
- }
+ virtio_snd_pcm_set_active(stream, start);
return cpu_to_le32(VIRTIO_SND_S_OK);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v5 10/10] hw/audio/virtio-sound: add missing vmstate fields
2026-08-03 8:11 [PATCH v5 00/10] hw/audio/virtio-sound: basic migration support Alexander Mikhalitsyn
` (8 preceding siblings ...)
2026-08-03 8:11 ` [PATCH v5 09/10] hw/audio/virtio-sound: introduce virtio_snd_set_active() Alexander Mikhalitsyn
@ 2026-08-03 8:11 ` Alexander Mikhalitsyn
9 siblings, 0 replies; 11+ messages in thread
From: Alexander Mikhalitsyn @ 2026-08-03 8:11 UTC (permalink / raw)
To: qemu-devel
Cc: Manos Pitsidianakis, Volker Rümelin, Marc-André Lureau,
Gerd Hoffmann, Daniel P . Berrangé, Alexander Mikhalitsyn,
Michael S. Tsirkin, Stéphane Graber, Alexander Mikhalitsyn
From: Volker Rümelin <vr_qemu@t-online.de>
The virtio-sound device is currently not migratable. Add the
missing VMSTATE fields, enable migration and reconnect the audio
streams after migration.
The queue_inuse[] array variables mimic the inuse variable in
struct VirtQueue which is private. They are needed to restart
the virtio queues after migration.
Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
[AM: trivial rebase changes]
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
v4:
- added assert(. > 0) before decremeting s->queue_inuse[.]
(suggested by Manos Pitsidianakis)
v3:
- added latency_bytes field to VMStateDescription
As suggested by Marc-André Lureau:
- removed the "rc" variable from virtio_snd_post_load()
- dropped info field from VMStateDescription, because
it can't be modified by guest and initialized only from realize
- added minimum_version_id/version_id so we can extend VMStateDescription
without breaking compatibility in the future
---
hw/audio/virtio-snd.c | 83 +++++++++++++++++++++++++++++++----
include/hw/audio/virtio-snd.h | 1 +
2 files changed, 76 insertions(+), 8 deletions(-)
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index 12473862d24..6df7ea0eb04 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -24,7 +24,6 @@
#include "qapi/error.h"
#include "hw/audio/virtio-snd.h"
-#define VIRTIO_SOUND_VM_VERSION 1
#define VIRTIO_SOUND_JACK_DEFAULT 0
#define VIRTIO_SOUND_STREAM_DEFAULT 2
#define VIRTIO_SOUND_CHMAP_DEFAULT 0
@@ -74,17 +73,40 @@ static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512)
| BIT(VIRTIO_SND_PCM_RATE_192000)
| BIT(VIRTIO_SND_PCM_RATE_384000);
+static const VMStateDescription vmstate_virtio_snd_stream = {
+ .name = "virtio-sound-stream",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(state, VirtIOSoundPCMStream),
+ VMSTATE_UINT32(params.buffer_bytes, VirtIOSoundPCMStream),
+ VMSTATE_UINT32(params.period_bytes, VirtIOSoundPCMStream),
+ VMSTATE_UINT32(params.features, VirtIOSoundPCMStream),
+ VMSTATE_UINT8(params.channels, VirtIOSoundPCMStream),
+ VMSTATE_UINT8(params.format, VirtIOSoundPCMStream),
+ VMSTATE_UINT8(params.rate, VirtIOSoundPCMStream),
+ VMSTATE_UINT32(latency_bytes, VirtIOSoundPCMStream),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static const VMStateDescription vmstate_virtio_snd_device = {
- .name = TYPE_VIRTIO_SND,
- .version_id = VIRTIO_SOUND_VM_VERSION,
- .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
+ .name = "virtio-sound-device",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(queue_inuse, VirtIOSound, VIRTIO_SND_VQ_MAX),
+ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(streams, VirtIOSound,
+ snd_conf.streams,
+ vmstate_virtio_snd_stream, VirtIOSoundPCMStream),
+ VMSTATE_END_OF_LIST()
+ },
};
static const VMStateDescription vmstate_virtio_snd = {
- .name = TYPE_VIRTIO_SND,
- .unmigratable = 1,
- .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
- .version_id = VIRTIO_SOUND_VM_VERSION,
+ .name = "virtio-sound",
+ .version_id = 1,
+ .minimum_version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_VIRTIO_DEVICE,
VMSTATE_END_OF_LIST()
@@ -813,6 +835,8 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
sizeof(virtio_snd_hdr));
virtqueue_push(cmd->vq, cmd->elem,
sizeof(virtio_snd_hdr) + cmd->payload_size);
+ g_assert(s->queue_inuse[VIRTIO_SND_VQ_CONTROL] > 0);
+ s->queue_inuse[VIRTIO_SND_VQ_CONTROL] -= 1;
virtio_notify(VIRTIO_DEVICE(s), cmd->vq);
}
@@ -859,6 +883,7 @@ static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
while (elem) {
+ s->queue_inuse[VIRTIO_SND_VQ_CONTROL] += 1;
cmd = g_new0(virtio_snd_ctrl_command, 1);
cmd->elem = elem;
cmd->vq = vq;
@@ -976,6 +1001,7 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
goto tx_err;
}
+ vsnd->queue_inuse[VIRTIO_SND_VQ_TX] += 1;
buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
buffer->elem = elem;
buffer->populated = false;
@@ -1060,6 +1086,7 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
goto rx_err;
}
+ vsnd->queue_inuse[VIRTIO_SND_VQ_RX] += 1;
buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
buffer->elem = elem;
buffer->vq = vq;
@@ -1200,6 +1227,8 @@ static inline void return_tx_buffer(VirtIOSoundPCMStream *stream,
virtqueue_push(buffer->vq,
buffer->elem,
sizeof(virtio_snd_pcm_status));
+ g_assert(stream->s->queue_inuse[VIRTIO_SND_VQ_TX] > 0);
+ stream->s->queue_inuse[VIRTIO_SND_VQ_TX] -= 1;
virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
QSIMPLEQ_REMOVE(&stream->queue,
buffer,
@@ -1293,6 +1322,8 @@ static inline void return_rx_buffer(VirtIOSoundPCMStream *stream,
virtqueue_push(buffer->vq,
buffer->elem,
sizeof(virtio_snd_pcm_status) + buffer->size);
+ g_assert(stream->s->queue_inuse[VIRTIO_SND_VQ_RX] > 0);
+ stream->s->queue_inuse[VIRTIO_SND_VQ_RX] -= 1;
virtio_notify(VIRTIO_DEVICE(stream->s), buffer->vq);
QSIMPLEQ_REMOVE(&stream->queue,
buffer,
@@ -1411,6 +1442,37 @@ static void virtio_snd_unrealize(DeviceState *dev)
virtio_cleanup(vdev);
}
+static int virtio_snd_post_load(VirtIODevice *vdev)
+{
+ VirtIOSound *s = VIRTIO_SND(vdev);
+ uint32_t i;
+
+ for (i = 0; i < s->snd_conf.streams; i++) {
+ struct VirtIOSoundPCMStream *stream;
+
+ stream = virtio_snd_pcm_get_stream(s, i);
+ if (virtio_snd_pcm_state_prepared(stream->state)) {
+ virtio_snd_pcm_open(stream);
+
+ if (stream->state == VIRTIO_SND_PCM_STATE_STARTED) {
+ virtio_snd_pcm_set_active(stream, true);
+ }
+ }
+ }
+
+ for (i = 0; i < VIRTIO_SND_VQ_MAX; i++) {
+ if (s->queue_inuse[i]) {
+ if (!virtqueue_rewind(s->queues[i], s->queue_inuse[i])) {
+ error_report(
+ "virtio-snd: could not rewind %u elements in queue %u",
+ s->queue_inuse[i], i);
+ }
+ s->queue_inuse[i] = 0;
+ }
+ }
+
+ return 0;
+}
static void virtio_snd_reset(VirtIODevice *vdev)
{
@@ -1442,6 +1504,10 @@ static void virtio_snd_reset(VirtIODevice *vdev)
virtio_snd_pcm_buffer_free(buffer);
}
}
+
+ for (uint32_t i = 0; i < VIRTIO_SND_VQ_MAX; i++) {
+ vsnd->queue_inuse[i] = 0;
+ }
}
static void virtio_snd_class_init(ObjectClass *klass, const void *data)
@@ -1455,6 +1521,7 @@ static void virtio_snd_class_init(ObjectClass *klass, const void *data)
dc->vmsd = &vmstate_virtio_snd;
vdc->vmsd = &vmstate_virtio_snd_device;
+ vdc->post_load = virtio_snd_post_load;
vdc->realize = virtio_snd_realize;
vdc->unrealize = virtio_snd_unrealize;
vdc->get_config = virtio_snd_get_config;
diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h
index 85d5d7c8619..384d2868c19 100644
--- a/include/hw/audio/virtio-snd.h
+++ b/include/hw/audio/virtio-snd.h
@@ -194,6 +194,7 @@ struct VirtIOSound {
VirtIODevice parent_obj;
VirtQueue *queues[VIRTIO_SND_VQ_MAX];
+ uint32_t queue_inuse[VIRTIO_SND_VQ_MAX];
uint64_t features;
VirtIOSoundPCMStream *streams;
AudioBackend *audio_be;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread