From: Joonho Lee <mynameisjune111@gmail.com>
To: linux-sound@vger.kernel.org
Cc: tiwai@suse.de, 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 v7 16/16] ALSA: virtio: serialize capture lifecycle and reset state
Date: Wed, 28 Oct 2026 18:41:32 +0900 [thread overview]
Message-ID: <20261028094132.20364-17-mynameisjune111@gmail.com> (raw)
In-Reply-To: <20261028094132.20364-1-mynameisjune111@gmail.com>
The staged capture paths can lose pending period notifications across
pause, keep counters based on the old ALSA position after RESET, and race
prepare with synchronous release. A RELEASE_SENT retry can also cancel
period work while holding the PCM lock, causing a lockdep cycle and hang.
Schedule pending period work after successful start or resume, rebase the
staged counters on RESET, and serialize process-context lifecycle calls
with a per-stream mutex. Keep trigger outside that mutex and wait for RX
without cancelling period work from the RELEASE_SENT trigger retry.
Notify an active runtime of resume failure even after transfer shutdown.
Invalidate late control responses with a reset generation. Revoke device
PREPARE permission at prepare entry and RELEASE completion. Allow cached
tails in DRAINING and released PAUSED capture without refilling RX. Remove
the successful paused-ACK threshold prediction; the preceding core change
marks synchronous cleanup on the actual stop transition.
Move runtime buffer allocation into hw_params after old RX cleanup under
the lifecycle mutex. Revoke transfer and copy access in hw_free before
synchronous cleanup and mapping release. Preserve request-owned DMA and
bounce-buffer lifetime protection.
Targeted QEMU tests cover pending notifications, RESET, paused forward and
wrapped reads, RELEASE retry, concurrent prepare, failed resume, late
control completion across bus freeze/restore, early prepare failure and
non-staged recovery. KASAN and lockdep report no errors in corrected runs.
NOMMU runtime and physical VZ/MOTU validation have not been repeated.
One-vCPU TCG contention still produces dsnoop client-threshold XRUNs on
both v6 and this candidate while the slave remains RUNNING. Both pass the
hardware-accelerated comparison. The cover letter records the failure
counts and the unresolved TCG worker wall-time difference.
Assisted-by: LLM
Signed-off-by: Joonho Lee <mynameisjune111@gmail.com>
---
diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c
index 852cc27..2fd8595 100644
--- a/sound/virtio/virtio_card.c
+++ b/sound/virtio/virtio_card.c
@@ -354,6 +354,7 @@ static void virtsnd_reset_vqs(struct virtio_snd *snd)
guard(spinlock_irqsave)(&queue->lock);
guard(spinlock)(&vss->lock);
+ vss->reset_epoch++;
vss->prepared = false;
vss->xfer_enabled = false;
vss->suspended = true;
diff --git a/sound/virtio/virtio_pcm.c b/sound/virtio/virtio_pcm.c
index b62c181..1fe8c50 100644
--- a/sound/virtio/virtio_pcm.c
+++ b/sound/virtio/virtio_pcm.c
@@ -443,6 +443,7 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd)
INIT_WORK(&vss->elapsed_period, virtsnd_pcm_period_elapsed);
init_waitqueue_head(&vss->msg_empty);
spin_lock_init(&vss->lock);
+ mutex_init(&vss->lifecycle_lock);
}
info = kzalloc_objs(*info, snd->nsubstreams);
@@ -576,7 +577,7 @@ int virtsnd_pcm_build_devs(struct virtio_snd *snd)
snd_pcm_set_ops(vpcm->pcm, i, &virtsnd_pcm_ops[i]);
}
- snd_pcm_set_managed_buffer_all(vpcm->pcm,
+ snd_pcm_lib_preallocate_pages_for_all(vpcm->pcm,
SNDRV_DMA_TYPE_VMALLOC, NULL,
0, 0);
}
diff --git a/sound/virtio/virtio_pcm.h b/sound/virtio/virtio_pcm.h
index 1da8f09..1140b4b 100644
--- a/sound/virtio/virtio_pcm.h
+++ b/sound/virtio/virtio_pcm.h
@@ -7,6 +7,7 @@
#define VIRTIO_SND_PCM_H
#include <linux/atomic.h>
+#include <linux/mutex.h>
#include <linux/virtio_config.h>
#include <linux/workqueue.h>
#include <sound/pcm.h>
@@ -43,6 +44,8 @@ enum virtio_pcm_pause_restart_state {
* @pcm_indirect: Kernel indirect pcm structure.
* @hw: Kernel ALSA substream hardware descriptor.
* @elapsed_period: Kernel work to handle the elapsed period state.
+ * @lifecycle_lock: Serializes prepare with synchronous release, outside trigger.
+ * @reset_epoch: Invalidates control replies from before device reset.
* @lock: Spinlock that protects fields shared by interrupt handlers and
* substream operators, including @stopped.
* @buffer_bytes: Current buffer size in bytes.
@@ -89,6 +92,8 @@ struct virtio_pcm_substream {
struct snd_pcm_hardware hw;
struct work_struct elapsed_period;
spinlock_t lock;
+ struct mutex lifecycle_lock;
+ u64 reset_epoch;
size_t buffer_bytes;
size_t hw_ptr;
size_t capture_ready;
diff --git a/sound/virtio/virtio_pcm_ops.c b/sound/virtio/virtio_pcm_ops.c
index 0685a00..573fab6 100644
--- a/sound/virtio/virtio_pcm_ops.c
+++ b/sound/virtio/virtio_pcm_ops.c
@@ -128,11 +128,14 @@ static const struct virtsnd_a2v_rate g_a2v_rate_map[] = {
};
static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream);
+static int virtsnd_pcm_sync_stop_locked(struct snd_pcm_substream *substream);
static int virtsnd_pcm_pause_restart_sync_stop(struct snd_pcm_substream *substream);
static void virtsnd_pcm_start_failed(struct virtio_pcm_substream *vss)
{
scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (vss->suspended)
+ return;
vss->xfer_enabled = false;
vss->stopped = true;
}
@@ -160,6 +163,22 @@ static bool virtsnd_pcm_pause_restart_transitioning(enum virtio_pcm_pause_restar
}
}
+static int virtsnd_pcm_send_sync(struct virtio_pcm_substream *vss,
+ struct virtio_snd_msg *msg)
+{
+ u64 epoch;
+ int rc;
+
+ scoped_guard(spinlock_irqsave, &vss->lock)
+ epoch = vss->reset_epoch;
+ rc = virtsnd_ctl_msg_send_sync(vss->snd, msg);
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (epoch != vss->reset_epoch)
+ return -ECANCELED;
+ }
+ return rc;
+}
+
static int
virtsnd_pcm_pause_restart_send(struct virtio_pcm_substream *vss,
struct virtio_snd_msg *msg,
@@ -169,12 +188,14 @@ virtsnd_pcm_pause_restart_send(struct virtio_pcm_substream *vss,
{
enum virtio_pcm_pause_restart_state state;
bool uncertain;
+ u64 epoch;
int rc;
if (!virtsnd_pcm_pause_restart_enabled(vss))
- return virtsnd_ctl_msg_send_sync(vss->snd, msg);
+ return virtsnd_pcm_send_sync(vss, msg);
scoped_guard(spinlock_irqsave, &vss->lock) {
+ epoch = vss->reset_epoch;
state = vss->pause_restart_state;
if (state == VIRTSND_PCM_PAUSE_RESTART_RESET_REQUIRED) {
rc = -EIO;
@@ -200,6 +221,8 @@ virtsnd_pcm_pause_restart_send(struct virtio_pcm_substream *vss,
uncertain = virtsnd_ctl_msg_uncertain(msg, rc);
virtsnd_ctl_msg_unref(msg);
scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (vss->reset_epoch != epoch)
+ return -ECANCELED;
if ((rc && uncertain) || vss->pause_restart_state != submitted_state) {
vss->pause_restart_state =
VIRTSND_PCM_PAUSE_RESTART_RESET_REQUIRED;
@@ -227,8 +250,9 @@ virtsnd_pcm_pause_restart_fail(struct virtio_pcm_substream *vss,
bool notify_xrun;
scoped_guard(spinlock_irqsave, &vss->lock) {
- notify_xrun = vss->runtime_active && vss->xfer_enabled &&
- !vss->xfer_xrun;
+ if (vss->suspended)
+ return;
+ notify_xrun = vss->runtime_active;
vss->xfer_enabled = false;
vss->stopped = true;
vss->xfer_xrun = true;
@@ -310,6 +334,7 @@ virtsnd_pcm_pause_release_flush(struct virtio_pcm_substream *vss,
VIRTSND_PCM_PAUSE_RESTART_RELEASE_SENT) {
rc = -ETIMEDOUT;
} else {
+ vss->prepared = false;
vss->pause_restart_state =
VIRTSND_PCM_PAUSE_RESTART_RELEASED;
}
@@ -353,7 +378,6 @@ static int virtsnd_pcm_open(struct snd_pcm_substream *substream)
scoped_guard(spinlock_irqsave, &vss->lock)
vss->stopped |= !!vss->msg_count;
- vss->suspended = false;
/*
* If the substream has already been used, then the I/O queue may be in
@@ -478,6 +502,11 @@ static int virtsnd_pcm_hw_params(struct snd_pcm_substream *substream,
bool stopped;
int rc;
+ guard(mutex)(&vss->lifecycle_lock);
+ /* Drain the previous buffer before ALSA can replace its mapping. */
+ rc = virtsnd_pcm_sync_stop_locked(substream);
+ if (rc)
+ return rc;
scoped_guard(spinlock_irqsave, &vss->lock)
vss->prepared = false;
@@ -505,6 +534,11 @@ static int virtsnd_pcm_hw_params(struct snd_pcm_substream *substream,
return -EBADFD;
}
+ rc = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (rc < 0)
+ return rc;
+ substream->runtime->buffer_changed = rc > 0;
+
rc = virtsnd_pcm_dev_set_params(vss, params_buffer_bytes(hw_params),
params_period_bytes(hw_params),
params_channels(hw_params),
@@ -539,9 +573,16 @@ static int virtsnd_pcm_hw_params(struct snd_pcm_substream *substream,
static int virtsnd_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+ int rc;
- scoped_guard(spinlock_irqsave, &vss->lock)
+ guard(mutex)(&vss->lifecycle_lock);
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ vss->stopped |= vss->prepared;
vss->prepared = false;
+ vss->runtime_active = false;
+ vss->xfer_enabled = false;
+ }
+ rc = virtsnd_pcm_sync_stop_locked(substream);
if (vss->capture_staging) {
scoped_guard(spinlock_irqsave, &vss->lock) {
@@ -567,17 +608,18 @@ static int virtsnd_pcm_hw_free(struct snd_pcm_substream *substream)
if (!virtsnd_pcm_msg_pending_num(vss))
virtsnd_pcm_msg_free(vss);
- return 0;
+ snd_pcm_lib_free_pages(substream);
+ return rc;
}
/**
- * virtsnd_pcm_prepare() - Prepare the PCM substream.
+ * virtsnd_pcm_prepare_locked() - Prepare the PCM substream.
* @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_locked(struct snd_pcm_substream *substream)
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
struct virtio_device *vdev = vss->snd->vdev;
@@ -585,6 +627,14 @@ static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
bool free_running;
int rc;
bool stopped;
+ bool suspended;
+ u64 epoch;
+
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ vss->prepared = false;
+ epoch = vss->reset_epoch;
+ suspended = vss->suspended;
+ }
if (pause_restart) {
enum virtio_pcm_pause_restart_state state;
@@ -607,7 +657,7 @@ static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
return -EBADFD;
}
- if (!vss->suspended) {
+ if (!suspended) {
if (virtsnd_pcm_msg_pending_num(vss)) {
dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n",
vss->sid);
@@ -630,6 +680,8 @@ static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
}
scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (epoch != vss->reset_epoch)
+ return -ECANCELED;
vss->prepared = false;
vss->buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
vss->hw_ptr = 0;
@@ -651,10 +703,15 @@ static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
}
rc = virtsnd_pcm_dev_prepare(vss);
- scoped_guard(spinlock_irqsave, &vss->lock)
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (epoch != vss->reset_epoch)
+ return -ECANCELED;
vss->prepared = !rc;
+ }
if (pause_restart) {
if (rc) {
+ if (rc == -ECANCELED)
+ return rc;
virtsnd_pcm_pause_restart_fail(vss, false);
} else {
scoped_guard(spinlock_irqsave, &vss->lock) {
@@ -667,6 +724,14 @@ static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
return rc;
}
+static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+
+ guard(mutex)(&vss->lifecycle_lock);
+ return virtsnd_pcm_prepare_locked(substream);
+}
+
static int virtsnd_pcm_pause_restart_start(struct virtio_pcm_substream *vss,
bool resume)
{
@@ -704,6 +769,8 @@ static int virtsnd_pcm_pause_restart_start(struct virtio_pcm_substream *vss,
if (resume) {
rc = virtsnd_pcm_pause_prepare(vss);
if (rc) {
+ if (rc == -ECANCELED)
+ return rc;
virtsnd_pcm_pause_restart_failed(vss);
return rc;
}
@@ -715,11 +782,14 @@ static int virtsnd_pcm_pause_restart_start(struct virtio_pcm_substream *vss,
rc = -EBADFD;
} else {
/* Preserve ring accounting, restarting only startup grace. */
+ vss->prepared = true;
vss->capture_started = false;
vss->capture_empty = 0;
}
}
if (rc) {
+ if (rc == -ECANCELED)
+ return rc;
virtsnd_pcm_pause_restart_failed(vss);
return rc;
}
@@ -738,6 +808,8 @@ static int virtsnd_pcm_pause_restart_start(struct virtio_pcm_substream *vss,
}
}
if (rc) {
+ if (rc == -ECANCELED)
+ return rc;
virtsnd_pcm_pause_restart_failed(vss);
return rc;
}
@@ -753,12 +825,18 @@ static int virtsnd_pcm_pause_restart_start(struct virtio_pcm_substream *vss,
VIRTSND_PCM_PAUSE_RESTART_START_SUBMITTED,
VIRTSND_PCM_PAUSE_RESTART_RUNNING);
if (rc) {
+ if (rc == -ECANCELED)
+ return rc;
virtsnd_pcm_pause_restart_failed(vss);
return rc;
}
- scoped_guard(spinlock_irqsave, &vss->lock)
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (vss->suspended)
+ return -ECANCELED;
vss->stopped = false;
+ }
+ virtsnd_pcm_schedule_period_elapsed(vss);
return 0;
}
@@ -771,20 +849,31 @@ static int virtsnd_pcm_pause_restart_pause(struct virtio_pcm_substream *vss)
scoped_guard(spinlock_irqsave, &vss->lock) {
state = vss->pause_restart_state;
- if (state == VIRTSND_PCM_PAUSE_RESTART_RELEASED)
+ if (state == VIRTSND_PCM_PAUSE_RESTART_RELEASED) {
+ if (vss->xfer_xrun)
+ virtsnd_pcm_schedule_period_elapsed(vss);
return vss->xfer_xrun ? -EPIPE : 0;
+ }
vss->xfer_enabled = false;
vss->stopped = true;
}
if (state == VIRTSND_PCM_PAUSE_RESTART_RELEASE_SENT) {
- rc = virtsnd_pcm_pause_restart_sync_stop(vss->substream);
- if (!rc) {
- guard(spinlock_irqsave)(&vss->lock);
- vss->stopped = true;
- if (vss->xfer_xrun)
- rc = -EPIPE;
+ unsigned int js = msecs_to_jiffies(virtsnd_msg_timeout_ms);
+
+ /* Trigger holds the PCM lock: never cancel the period worker here. */
+ if (!wait_event_timeout(vss->msg_empty,
+ !virtsnd_pcm_msg_pending_num(vss), js))
+ return -ETIMEDOUT;
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (vss->suspended)
+ return -ECANCELED;
+ vss->prepared = false;
+ vss->pause_restart_state = VIRTSND_PCM_PAUSE_RESTART_RELEASED;
+ rc = vss->xfer_xrun ? -EPIPE : 0;
}
+ if (rc)
+ virtsnd_pcm_pause_restart_failed(vss);
return rc;
}
@@ -807,6 +896,8 @@ static int virtsnd_pcm_pause_restart_pause(struct virtio_pcm_substream *vss)
VIRTSND_PCM_PAUSE_RESTART_STOP_SUBMITTED,
VIRTSND_PCM_PAUSE_RESTART_STOPPED);
if (rc) {
+ if (rc == -ECANCELED)
+ return rc;
virtsnd_pcm_pause_restart_failed(vss);
return rc;
}
@@ -821,6 +912,8 @@ static int virtsnd_pcm_pause_restart_pause(struct virtio_pcm_substream *vss)
BIT(VIRTSND_PCM_PAUSE_RESTART_STOPPED) |
BIT(VIRTSND_PCM_PAUSE_RESTART_PREPARED));
if (rc) {
+ if (rc == -ECANCELED)
+ return rc;
virtsnd_pcm_pause_restart_failed(vss);
return rc;
}
@@ -836,6 +929,8 @@ static int virtsnd_pcm_pause_restart_stop(struct virtio_pcm_substream *vss,
int rc;
scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (!vss->prepared && !vss->msg_count)
+ return 0;
if (suspend)
vss->suspended = true;
vss->xfer_enabled = false;
@@ -861,7 +956,7 @@ static int virtsnd_pcm_pause_restart_stop(struct virtio_pcm_substream *vss,
BIT(VIRTSND_PCM_PAUSE_RESTART_RUNNING),
VIRTSND_PCM_PAUSE_RESTART_STOP_SUBMITTED,
VIRTSND_PCM_PAUSE_RESTART_STOPPED);
- if (rc)
+ if (rc && rc != -ECANCELED)
virtsnd_pcm_pause_restart_failed(vss);
return rc;
@@ -879,7 +974,6 @@ static int virtsnd_pcm_pause_restart_stop(struct virtio_pcm_substream *vss,
static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command)
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
- struct virtio_snd *snd = vss->snd;
struct virtio_snd_queue *queue;
struct virtio_snd_msg *msg;
int rc = 0;
@@ -945,10 +1039,12 @@ static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command)
return -ENOMEM;
}
- rc = virtsnd_ctl_msg_send_sync(snd, msg);
- if (rc)
+ rc = virtsnd_pcm_send_sync(vss, msg);
+ if (rc && rc != -ECANCELED)
virtsnd_pcm_start_failed(vss);
+ if (!rc && vss->capture_staging)
+ virtsnd_pcm_schedule_period_elapsed(vss);
return rc;
case SNDRV_PCM_TRIGGER_SUSPEND:
scoped_guard(spinlock_irqsave, &vss->lock)
@@ -957,6 +1053,8 @@ static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command)
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (!vss->prepared)
+ return 0;
if (command != SNDRV_PCM_TRIGGER_PAUSE_PUSH)
vss->stopped = true;
vss->xfer_enabled = false;
@@ -967,7 +1065,7 @@ static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command)
if (!msg)
return -ENOMEM;
- return virtsnd_ctl_msg_send_sync(snd, msg);
+ return virtsnd_pcm_send_sync(vss, msg);
default:
return -EINVAL;
}
@@ -1023,6 +1121,8 @@ virtsnd_pcm_pause_restart_sync_stop(struct snd_pcm_substream *substream)
VIRTSND_PCM_PAUSE_RESTART_STOP_SUBMITTED,
VIRTSND_PCM_PAUSE_RESTART_STOPPED);
if (rc) {
+ if (rc == -ECANCELED)
+ return rc;
virtsnd_pcm_pause_restart_fail(vss, false);
return rc;
}
@@ -1039,11 +1139,14 @@ virtsnd_pcm_pause_restart_sync_stop(struct snd_pcm_substream *substream)
return rc;
}
scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (vss->suspended)
+ return -ECANCELED;
if (vss->msg_count) {
rc = -ETIMEDOUT;
} else {
vss->pause_restart_state =
VIRTSND_PCM_PAUSE_RESTART_RELEASED;
+ vss->prepared = false;
vss->stopped = false;
rc = 0;
}
@@ -1063,18 +1166,24 @@ virtsnd_pcm_pause_restart_sync_stop(struct snd_pcm_substream *substream)
BIT(VIRTSND_PCM_PAUSE_RESTART_PREPARED) |
BIT(VIRTSND_PCM_PAUSE_RESTART_STOPPED));
if (rc) {
+ if (rc == -ECANCELED)
+ return rc;
virtsnd_pcm_pause_restart_fail(vss, false);
return rc;
}
- scoped_guard(spinlock_irqsave, &vss->lock)
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (vss->suspended)
+ return -ECANCELED;
+ vss->prepared = false;
vss->stopped = false;
+ }
return 0;
}
/**
- * virtsnd_pcm_sync_stop() - Synchronous PCM substream stop.
+ * virtsnd_pcm_sync_stop_locked() - Synchronous PCM substream stop.
* @substream: Kernel ALSA substream.
*
* The function can be called both from the upper level or from the driver
@@ -1083,7 +1192,7 @@ virtsnd_pcm_pause_restart_sync_stop(struct snd_pcm_substream *substream)
* Context: Process context. Takes and releases the VirtIO substream spinlock.
* Return: 0 on success, -errno on failure.
*/
-static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream)
+static int virtsnd_pcm_sync_stop_locked(struct snd_pcm_substream *substream)
{
struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
struct virtio_snd *snd = vss->snd;
@@ -1097,7 +1206,7 @@ static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream)
scoped_guard(spinlock_irqsave, &vss->lock) {
vss->xfer_enabled = false;
- need_release = vss->stopped || vss->msg_count;
+ need_release = vss->prepared || vss->stopped || vss->msg_count;
if (need_release)
vss->stopped = true;
}
@@ -1112,7 +1221,7 @@ static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream)
return -ENOMEM;
}
- rc = virtsnd_ctl_msg_send_sync(snd, msg);
+ rc = virtsnd_pcm_send_sync(vss, msg);
if (rc) {
substream->runtime->stop_operating = true;
return rc;
@@ -1135,8 +1244,12 @@ static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream)
return !rc ? -ETIMEDOUT : rc;
}
- scoped_guard(spinlock_irqsave, &vss->lock)
+ scoped_guard(spinlock_irqsave, &vss->lock) {
+ if (vss->suspended)
+ return -ECANCELED;
+ vss->prepared = false;
vss->stopped = false;
+ }
return 0;
}
@@ -1299,8 +1412,13 @@ static int virtsnd_pcm_capture_staged_ack(struct virtio_pcm_substream *vss,
u64 next_acked_total;
bool free_running;
- if (!vss->prepared || !vss->runtime_active || !runtime || !runtime->control ||
- !runtime->dma_area)
+ if (!vss->runtime_active || !runtime || !runtime->control ||
+ !runtime->dma_area || vss->suspended)
+ return -EBADFD;
+ if (!vss->prepared && runtime->state != SNDRV_PCM_STATE_DRAINING &&
+ !(runtime->state == SNDRV_PCM_STATE_PAUSED &&
+ virtsnd_pcm_pause_restart_enabled(vss) &&
+ vss->pause_restart_state == VIRTSND_PCM_PAUSE_RESTART_RELEASED))
return -EBADFD;
if (vss->xfer_xrun)
return -EPIPE;
@@ -1376,9 +1494,7 @@ static int virtsnd_pcm_cp_ack(struct snd_pcm_substream *substream)
if (vss->capture_staging) {
rc = virtsnd_pcm_capture_staged_ack(vss, runtime);
- if (rc == -EPIPE ||
- (!rc && runtime->state == SNDRV_PCM_STATE_PAUSED &&
- snd_pcm_capture_avail(runtime) >= runtime->stop_threshold)) {
+ if (rc == -EPIPE) {
guard(spinlock_irqsave)(&vss->lock);
virtsnd_pcm_capture_arm_paused_stop(vss);
}
@@ -1387,7 +1503,7 @@ static int virtsnd_pcm_cp_ack(struct snd_pcm_substream *substream)
scoped_guard(spinlock_irqsave, &queue->lock) {
guard(spinlock)(&vss->lock);
- if (!vss->prepared)
+ if (!vss->prepared && runtime->state != SNDRV_PCM_STATE_DRAINING)
return -EBADFD;
transfer.indirect = vss->pcm_indirect;
if (vss->stopped && runtime->state != SNDRV_PCM_STATE_DRAINING) {
@@ -1415,12 +1531,52 @@ static int virtsnd_pcm_cp_ack(struct snd_pcm_substream *substream)
return 0;
}
+static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream)
+{
+ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+
+ guard(mutex)(&vss->lifecycle_lock);
+ return virtsnd_pcm_sync_stop_locked(substream);
+}
+
+static int virtsnd_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ size_t position;
+ size_t period;
+ int rc;
+
+ if (cmd != SNDRV_PCM_IOCTL1_RESET || !vss->capture_staging)
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+
+ rc = snd_pcm_lib_ioctl(substream, cmd, arg);
+ if (rc)
+ return rc;
+ guard(pcm_stream_lock_irqsave)(substream);
+ runtime->status->hw_ptr %= runtime->buffer_size;
+ position = frames_to_bytes(runtime, runtime->status->hw_ptr);
+ period = snd_pcm_lib_period_bytes(substream);
+ guard(spinlock_irqsave)(&vss->lock);
+ vss->hw_ptr = position;
+ vss->capture_copied_total = position;
+ vss->capture_acked_total = position;
+ vss->capture_pointer_seen_total = position;
+ vss->capture_ready = 0;
+ vss->capture_notified_periods = period ? position / period : 0;
+ vss->capture_callback_snapshot = 0;
+ vss->capture_callback_snapshot_active = false;
+ vss->pcm_indirect.appl_ptr = runtime->status->hw_ptr;
+ return 0;
+}
+
/* PCM substream operators map. */
const struct snd_pcm_ops virtsnd_pcm_ops[] = {
{
.open = virtsnd_pcm_open,
.close = virtsnd_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
+ .ioctl = virtsnd_pcm_ioctl,
.hw_params = virtsnd_pcm_hw_params,
.hw_free = virtsnd_pcm_hw_free,
.prepare = virtsnd_pcm_prepare,
@@ -1432,7 +1588,7 @@ const struct snd_pcm_ops virtsnd_pcm_ops[] = {
{
.open = virtsnd_pcm_open,
.close = virtsnd_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
+ .ioctl = virtsnd_pcm_ioctl,
.hw_params = virtsnd_pcm_hw_params,
.hw_free = virtsnd_pcm_hw_free,
.prepare = virtsnd_pcm_prepare,
prev parent reply other threads:[~2026-10-28 9:42 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 9:41 [PATCH v7 00/16] ALSA: virtio: staged capture and dsnoop recovery Joonho Lee
2026-10-28 9:41 ` [PATCH v7 01/16] ALSA: virtio: Propagate PCM send failures through ACK Joonho Lee
2026-10-28 9:41 ` [PATCH v7 02/16] ALSA: virtio: Recover PCM state after a failed start Joonho Lee
2026-10-28 9:41 ` [PATCH v7 03/16] ALSA: virtio: Retain queued messages through device reset Joonho Lee
2026-10-28 9:41 ` [PATCH v7 04/16] ALSA: virtio: Keep PCM DMA buffers alive through device ownership Joonho Lee
2026-10-28 9:41 ` [PATCH v7 05/16] ALSA: virtio: Keep control payloads alive through DMA completion Joonho Lee
2026-10-28 9:41 ` [PATCH v7 06/16] ALSA: virtio: wait through signals for PCM teardown Joonho Lee
2026-10-28 9:41 ` [PATCH v7 07/16] ALSA: virtio: stage VZ capture and restart paused streams Joonho Lee
2026-10-28 9:41 ` [PATCH v7 08/16] ALSA: virtio: account staged capture reads before RX refill Joonho Lee
2026-10-28 9:41 ` [PATCH v7 09/16] ALSA: virtio: prioritize staged capture period work Joonho Lee
2026-10-28 9:41 ` [PATCH v7 10/16] ALSA: pcm: mark stop-threshold accesses for capture policy sampling Joonho Lee
2026-10-28 9:41 ` [PATCH v7 11/16] ALSA: virtio-snd: fix dsnoop capture with staged buffers Joonho Lee
2026-10-28 9:41 ` [PATCH v7 12/16] ALSA: virtio: complete control transitions and preserve drain tail Joonho Lee
2026-10-28 9:41 ` [PATCH v7 13/16] ALSA: virtio: fence staged capture state and recovery Joonho Lee
2026-10-28 9:41 ` [PATCH v7 14/16] docs: describe virtio-sound capture options Joonho Lee
2026-10-28 9:41 ` [PATCH v7 15/16] ALSA: pcm: synchronize stopped paused streams Joonho Lee
2026-10-28 9:41 ` Joonho Lee [this message]
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=20261028094132.20364-17-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.de \
--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