From: Joonho Lee <mynameisjune111@gmail.com>
To: linux-sound@vger.kernel.org
Cc: tiwai@suse.com, perex@perex.cz, anton.yakovlev@opensynergy.com,
mst@redhat.com, virtualization@lists.linux.dev,
linux-kernel@vger.kernel.org, corbet@lwn.net,
skhan@linuxfoundation.org, rdunlap@infradead.org,
linux-doc@vger.kernel.org, Joonho Lee <mynameisjune111@gmail.com>
Subject: [PATCH v24 06/16] ALSA: virtio: propagate playback send failures through ACK
Date: Wed, 11 Nov 2026 09:05:33 +0900 [thread overview]
Message-ID: <20261111000543.28550-7-mynameisjune111@gmail.com> (raw)
In-Reply-To: <20261111000543.28550-1-mynameisjune111@gmail.com>
Propagate playback submission failures through ACK and commit indirect
accounting only after transport submission succeeds. Keep the PCM state
and buffer-access checks synchronized with playback transfer admission.
The existing capture ACK callback and initial queue reservation behavior
remain together until the request-owned capture conversion in patch 12.
That conversion introduces RX reservation, validation and refill as one
contract.
Assisted-by: LLM
Signed-off-by: Joonho Lee <mynameisjune111@gmail.com>
---
sound/virtio/virtio_pcm.h | 6 +
sound/virtio/virtio_pcm_msg.c | 12 ++
sound/virtio/virtio_pcm_ops.c | 237 ++++++++++++++++++++++++++++++++++++-----
3 files changed, 228 insertions(+), 27 deletions(-)
diff --git a/sound/virtio/virtio_pcm.h b/sound/virtio/virtio_pcm.h
--- a/sound/virtio/virtio_pcm.h
+++ b/sound/virtio/virtio_pcm.h
@@ -33,6 +33,10 @@
* @xfer_xrun: Data underflow/overflow state (0 - no xrun, 1 - xrun).
* @stopped: True if the substream is stopped and must be released on the device
* side.
+ * @prepared: True after device PREPARE and the core pointer reset succeed;
+ * protected by @lock.
+ * @preparing: ACK gate through teardown and prepare's core reset;
+ * protected by @lock.
* @suspended: True if the substream is suspended and must be reconfigured on
* the device side at resume.
* @msgs: Allocated I/O messages.
@@ -56,6 +60,8 @@
bool xfer_enabled;
bool xfer_xrun;
bool stopped;
+ bool prepared;
+ bool preparing;
bool suspended;
struct virtio_pcm_msg **msgs;
unsigned int nmsgs;
diff --git a/sound/virtio/virtio_pcm_msg.c b/sound/virtio/virtio_pcm_msg.c
--- a/sound/virtio/virtio_pcm_msg.c
+++ b/sound/virtio/virtio_pcm_msg.c
@@ -260,6 +260,18 @@
dev_err(&vdev->dev,
"SID %u: failed to send I/O message\n",
vss->sid);
+ msg->length -= n;
+
+ /*
+ * Do not leave earlier messages queued without notifying
+ * the device when a later message could not be added.
+ */
+ if (msg_count != vss->msg_count &&
+ !(vss->features &
+ (1U << VIRTIO_SND_PCM_F_MSG_POLLING)) &&
+ virtqueue_kick_prepare(vqueue))
+ virtqueue_notify(vqueue);
+
return rc;
}
diff --git a/sound/virtio/virtio_pcm_ops.c b/sound/virtio/virtio_pcm_ops.c
--- a/sound/virtio/virtio_pcm_ops.c
+++ b/sound/virtio/virtio_pcm_ops.c
@@ -97,6 +97,38 @@
static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream);
/**
+ * virtsnd_pcm_close_ack() - Update PCM prepare admission state.
+ * @substream: Kernel ALSA substream.
+ * @preparing: True while driver prepare and the core pointer reset are active.
+ *
+ * Context: Process context.
+ */
+static void virtsnd_pcm_close_ack(struct snd_pcm_substream *substream,
+ bool preparing)
+{
+ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+
+ scoped_guard(pcm_stream_lock_irqsave, substream) {
+ guard(spinlock_irqsave)(&vss->lock);
+ vss->prepared = false;
+ vss->xfer_enabled = false;
+ vss->preparing = preparing;
+ }
+}
+
+/* Keep completed RX data valid while RELEASE drains the device queue. */
+static void virtsnd_pcm_block_ack(struct snd_pcm_substream *substream)
+{
+ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+
+ scoped_guard(pcm_stream_lock_irqsave, substream) {
+ guard(spinlock_irqsave)(&vss->lock);
+ vss->preparing = true;
+ vss->xfer_enabled = false;
+ }
+}
+
+/**
* virtsnd_pcm_open() - Open the PCM substream.
* @substream: Kernel ALSA substream.
*
@@ -115,7 +147,8 @@
snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
- vss->stopped = !!virtsnd_pcm_msg_pending_num(vss);
+ scoped_guard(spinlock_irqsave, &vss->lock)
+ vss->stopped = !!vss->msg_count;
vss->suspended = false;
/*
@@ -219,6 +252,8 @@
struct virtio_device *vdev = vss->snd->vdev;
int rc;
+ virtsnd_pcm_close_ack(substream, false);
+
if (virtsnd_pcm_msg_pending_num(vss)) {
dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n",
vss->sid);
@@ -254,6 +289,8 @@
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+ virtsnd_pcm_close_ack(substream, false);
+
/* If the queue is flushed, we can safely free the messages here. */
if (!virtsnd_pcm_msg_pending_num(vss))
virtsnd_pcm_msg_free(vss);
@@ -262,17 +299,18 @@
}
/**
- * virtsnd_pcm_prepare() - Prepare the PCM substream.
+ * virtsnd_pcm_prepare_device() - Prepare the PCM substream on the device.
* @substream: Kernel ALSA substream.
*
* Context: Process context.
* Return: 0 on success, -errno on failure.
*/
-static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
+static int virtsnd_pcm_prepare_device(struct snd_pcm_substream *substream)
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
struct virtio_device *vdev = vss->snd->vdev;
struct virtio_snd_msg *msg;
+ int rc;
if (!vss->suspended) {
if (virtsnd_pcm_msg_pending_num(vss)) {
@@ -287,16 +325,14 @@
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
unsigned int period_bytes = snd_pcm_lib_period_bytes(substream);
- int rc;
rc = virtsnd_pcm_dev_set_params(vss, buffer_bytes, period_bytes,
- runtime->channels,
- runtime->format, runtime->rate);
+ runtime->channels,
+ runtime->format, runtime->rate);
if (rc)
return rc;
}
- vss->xfer_xrun = false;
vss->suspended = false;
vss->msg_count = 0;
virtsnd_pcm_msg_reset(vss);
@@ -311,7 +347,33 @@
if (!msg)
return -ENOMEM;
- return virtsnd_ctl_msg_send_sync(vss->snd, msg);
+ rc = virtsnd_ctl_msg_send_sync(vss->snd, msg);
+ if (rc)
+ return rc;
+
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ vss->xfer_xrun = false;
+ }
+ return 0;
+}
+
+/* Keep ACK closed through the core's locked pointer reset. */
+static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ virtsnd_pcm_close_ack(substream, true);
+ return virtsnd_pcm_prepare_device(substream);
+}
+
+/* Complete admission only after the linked core prepare action ends. */
+static void virtsnd_pcm_prepare_complete(
+ struct snd_pcm_substream *substream, bool success)
+{
+ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ vss->prepared = success;
+ vss->preparing = false;
+ }
}
/**
@@ -338,6 +400,8 @@
scoped_guard(spinlock_irqsave, &queue->lock) {
guard(spinlock)(&vss->lock);
+ if (vss->preparing || !vss->prepared)
+ return -EBADFD;
if (vss->direction == SNDRV_PCM_STREAM_CAPTURE)
rc = virtsnd_pcm_msg_send(vss, 0, vss->buffer_bytes);
if (rc)
@@ -356,10 +420,12 @@
return virtsnd_ctl_msg_send_sync(snd, msg);
case SNDRV_PCM_TRIGGER_SUSPEND:
- vss->suspended = true;
+ scoped_guard(spinlock_irqsave, &vss->lock)
+ vss->suspended = true;
fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
- vss->stopped = true;
+ scoped_guard(spinlock_irqsave, &vss->lock)
+ vss->stopped = true;
fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
scoped_guard(spinlock_irqsave, &vss->lock) {
@@ -393,11 +459,16 @@
struct virtio_snd *snd = vss->snd;
struct virtio_snd_msg *msg;
unsigned int js = msecs_to_jiffies(virtsnd_msg_timeout_ms);
+ bool stopped;
int rc;
+ virtsnd_pcm_block_ack(substream);
+
cancel_work_sync(&vss->elapsed_period);
- if (!vss->stopped)
+ scoped_guard(spinlock_irqsave, &vss->lock)
+ stopped = vss->stopped;
+ if (!stopped)
return 0;
msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_RELEASE,
@@ -425,7 +496,8 @@
return !rc ? -ETIMEDOUT : rc;
}
- vss->stopped = false;
+ scoped_guard(spinlock_irqsave, &vss->lock)
+ vss->stopped = false;
return 0;
}
@@ -442,6 +514,10 @@
virtsnd_pcm_pb_pointer(struct snd_pcm_substream *substream)
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+
+ scoped_guard(spinlock_irqsave, &vss->lock)
+ if (vss->xfer_xrun)
+ return SNDRV_PCM_POS_XRUN;
return snd_pcm_indirect_playback_pointer(substream,
&vss->pcm_indirect,
@@ -461,41 +537,144 @@
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+ scoped_guard(spinlock_irqsave, &vss->lock)
+ if (vss->xfer_xrun)
+ return SNDRV_PCM_POS_XRUN;
+
return snd_pcm_indirect_capture_pointer(substream,
&vss->pcm_indirect,
vss->hw_ptr);
}
+struct virtsnd_pcm_transfer {
+ struct snd_pcm_indirect indirect;
+ int error;
+};
+
static void virtsnd_pcm_trans_copy(struct snd_pcm_substream *substream,
struct snd_pcm_indirect *rec, size_t bytes)
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+ struct virtsnd_pcm_transfer *transfer =
+ container_of(rec, struct virtsnd_pcm_transfer, indirect);
+
+ if (transfer->error)
+ return;
+
+ transfer->error = virtsnd_pcm_msg_send(vss, rec->sw_data, bytes);
+}
+
+/* Caller holds vss->lock. */
+static void virtsnd_pcm_transfer_failed(struct virtio_pcm_substream *vss)
+{
+ vss->xfer_enabled = false;
+ vss->stopped = true;
+ vss->xfer_xrun = true;
+ vss->substream->runtime->stop_operating = true;
+}
+
+/* The caller holds the PCM lock, also used by DROP and state transitions. */
+static int virtsnd_pcm_ack_state(struct snd_pcm_substream *substream)
+{
+ switch (substream->runtime->state) {
+ case SNDRV_PCM_STATE_XRUN:
+ /* The member is already XRUN; avoid stopping its linked group twice. */
+ return -EBADFD;
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_PAUSED:
+ case SNDRV_PCM_STATE_DRAINING:
+ if (atomic_read(&substream->runtime->buffer_accessing) < 0)
+ return -EBADFD;
+ return 0;
+ default:
+ return -EBADFD;
+ }
+}
+
+static int virtsnd_pcm_pb_ack(struct snd_pcm_substream *substream)
+{
+ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+ struct virtio_snd_queue *queue = virtsnd_pcm_queue(vss);
+ struct virtsnd_pcm_transfer transfer = {};
+ int rc;
+
+ rc = virtsnd_pcm_ack_state(substream);
+ if (rc)
+ return rc;
+
+ scoped_guard(spinlock_irqsave, &queue->lock) {
+ guard(spinlock)(&vss->lock);
+ if (vss->preparing || !vss->prepared)
+ return -EBADFD;
+ if (vss->stopped)
+ return -EBADFD;
+ if (vss->xfer_xrun) {
+ rc = -EPIPE;
+ } else {
+ transfer.indirect = vss->pcm_indirect;
+ rc = snd_pcm_indirect_playback_transfer(substream,
+ &transfer.indirect,
+ virtsnd_pcm_trans_copy);
+ if (!rc)
+ rc = transfer.error;
+ }
+ if (rc)
+ virtsnd_pcm_transfer_failed(vss);
+ else
+ vss->pcm_indirect = transfer.indirect;
+ }
+ if (rc) {
+ dev_err(&vss->snd->vdev->dev,
+ "SID %u: playback ACK transfer failed: %d\n",
+ vss->sid, rc);
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static void virtsnd_pcm_legacy_capture_copy(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect *rec, size_t bytes)
+{
+ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
virtsnd_pcm_msg_send(vss, rec->sw_data, bytes);
}
-static int virtsnd_pcm_pb_ack(struct snd_pcm_substream *substream)
+static int virtsnd_pcm_cp_ack(struct snd_pcm_substream *substream)
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
struct virtio_snd_queue *queue = virtsnd_pcm_queue(vss);
-
+ int rc;
+
+ rc = virtsnd_pcm_ack_state(substream);
+ if (rc)
+ return rc;
guard(spinlock_irqsave)(&queue->lock);
guard(spinlock)(&vss->lock);
-
- return snd_pcm_indirect_playback_transfer(substream, &vss->pcm_indirect,
- virtsnd_pcm_trans_copy);
-}
-
-static int virtsnd_pcm_cp_ack(struct snd_pcm_substream *substream)
-{
- struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
- struct virtio_snd_queue *queue = virtsnd_pcm_queue(vss);
-
- guard(spinlock_irqsave)(&queue->lock);
- guard(spinlock)(&vss->lock);
+ if (vss->preparing || !vss->prepared)
+ return -EBADFD;
+ if (vss->xfer_xrun)
+ return -EPIPE;
+ if (vss->stopped &&
+ substream->runtime->state != SNDRV_PCM_STATE_DRAINING)
+ return -EBADFD;
return snd_pcm_indirect_capture_transfer(substream, &vss->pcm_indirect,
- virtsnd_pcm_trans_copy);
+ virtsnd_pcm_legacy_capture_copy);
+}
+
+/**
+ * virtsnd_pcm_reset() - Reset PCM pointers.
+ * @substream: Kernel ALSA substream.
+ *
+ * Context: PCM stream lock held.
+ * Return: 0 on success, -errno on failure.
+ */
+static int virtsnd_pcm_reset(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_reset(substream);
}
/* PCM substream operators map. */
@@ -507,6 +686,8 @@
.hw_params = virtsnd_pcm_hw_params,
.hw_free = virtsnd_pcm_hw_free,
.prepare = virtsnd_pcm_prepare,
+ .prepare_complete = virtsnd_pcm_prepare_complete,
+ .reset = virtsnd_pcm_reset,
.trigger = virtsnd_pcm_trigger,
.sync_stop = virtsnd_pcm_sync_stop,
.pointer = virtsnd_pcm_pb_pointer,
@@ -519,6 +700,8 @@
.hw_params = virtsnd_pcm_hw_params,
.hw_free = virtsnd_pcm_hw_free,
.prepare = virtsnd_pcm_prepare,
+ .prepare_complete = virtsnd_pcm_prepare_complete,
+ .reset = virtsnd_pcm_reset,
.trigger = virtsnd_pcm_trigger,
.sync_stop = virtsnd_pcm_sync_stop,
.pointer = virtsnd_pcm_cp_pointer,
next prev parent reply other threads:[~2026-11-11 0:06 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 0:05 [PATCH v24 00/16] ALSA: virtio: serialize PCM recovery and preserve capture and playback progress Joonho Lee
2026-11-11 0:05 ` [PATCH v24 01/16] ALSA: pcm: mark stop-threshold accesses for capture policy sampling Joonho Lee
2026-11-11 0:05 ` [PATCH v24 02/16] ALSA: pcm: report partial progress under the stream lock Joonho Lee
2026-11-11 0:05 ` [PATCH v24 03/16] ALSA: pcm: add an optional reset operation " Joonho Lee
2026-11-11 0:05 ` [PATCH v24 04/16] ALSA: pcm: notify drivers after linked prepare completes Joonho Lee
2026-11-11 0:05 ` [PATCH v24 05/16] ALSA: pcm: serialize linked buffer actions Joonho Lee
2026-11-11 0:05 ` Joonho Lee [this message]
2026-11-11 0:05 ` [PATCH v24 07/16] ALSA: virtio: Recover PCM state after a failed start Joonho Lee
2026-11-11 0:05 ` [PATCH v24 08/16] ALSA: virtio: Retain queued messages through device reset Joonho Lee
2026-11-11 0:05 ` [PATCH v24 09/16] ALSA: virtio: bound PCM transfers and retain DMA buffers Joonho Lee
2026-11-11 0:05 ` [PATCH v24 10/16] ALSA: virtio: Keep control payloads alive through DMA completion Joonho Lee
2026-11-11 0:05 ` [PATCH v24 11/16] ALSA: virtio: wait through signals for PCM teardown Joonho Lee
2026-11-11 0:05 ` [PATCH v24 12/16] ALSA: virtio: use request-owned capture and serialize recovery Joonho Lee
2026-11-11 0:05 ` [PATCH v24 13/16] ALSA: virtio: prioritize capture period work Joonho Lee
2026-11-11 0:05 ` [PATCH v24 14/16] docs: describe virtio-sound capture options Joonho Lee
2026-11-11 0:05 ` [PATCH v24 15/16] ALSA: virtio: reject invalid playback completions Joonho Lee
2026-11-11 0:05 ` [PATCH v24 16/16] ALSA: virtio: preserve playback progress across pause Joonho Lee
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261111000543.28550-7-mynameisjune111@gmail.com \
--to=mynameisjune111@gmail.com \
--cc=anton.yakovlev@opensynergy.com \
--cc=corbet@lwn.net \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=mst@redhat.com \
--cc=perex@perex.cz \
--cc=rdunlap@infradead.org \
--cc=skhan@linuxfoundation.org \
--cc=tiwai@suse.com \
--cc=virtualization@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®