From: sashiko-bot@kernel.org
To: "Joonho Lee" <mynameisjune111@gmail.com>, linux-sound@vger.kernel.org
Cc: virtualization@lists.linux.dev,
"Michael S. Tsirkin" <mst@redhat.com>,
"Eugenio Perez" <eperezma@redhat.com>
Subject: Re: [PATCH v12 10/12] ALSA: virtio: add staged capture and serialize PCM lifecycle accounting
Date: Tue, 03 Nov 2026 00:50:58 +0000 [thread overview]
Message-ID: <sashiko-outbox-194389@kernel.org> (raw)
In-Reply-To: <20261103000403.67106-11-mynameisjune111@gmail.com>
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [High] ALSA: virtio: broken indirect ring state in virtsnd_pcm_playback_reset()
- [High] ALSA: virtio: post-stop capture RX not retired in virtsnd_pcm_msg_complete()
- [Medium] ALSA: virtio: missing prepared and preparing checks in virtsnd_pcm_cp_pointer()
- [Medium] ALSA: virtio: vss->preparing gate leaked on prepare failure and reset races
- [Medium] ALSA: virtio: stale vss->suspended flag across open and virtsnd_pcm_hw_params()
- [Medium] ALSA: virtio: sub-byte frame truncation in virtsnd_pcm_capture_staged_ack()
- [Medium] ALSA: virtio: missing state and xrun checks in virtsnd_pcm_pb_ack() and pointer
- [Medium] ALSA: virtio: stale vss->suspended across virtsnd_pcm_open() and hw_params()
- [Medium] ALSA: virtio: virtsnd_pcm_pause_restart_start() returns -EBADFD on xfer_xrun
- [Medium] ALSA: virtio: incomplete xrun and pointer rebase in virtsnd_pcm_reset()
--
commit aed4d30e1eeffd3a8fd687d7393b3bfcaedf5c71
Author: Joonho Lee <mynameisjune111@gmail.com>
ALSA: virtio: add staged capture and serialize PCM lifecycle accounting
This commit adds opt-in staged PCM capture using request-owned DMA buffers and
serializes PCM lifecycle, reset, and acknowledgment accounting under the queue
and substream locks.
Link: https://sashiko.dev/#/patchset/20261031020855.2402-1-mynameisjune111%40gmail.com
> diff --git a/sound/virtio/virtio_pcm_msg.c b/sound/virtio/virtio_pcm_msg.c
> index b94ab24f5ffef..ed340ecfb289e 100644
> --- a/sound/virtio/virtio_pcm_msg.c
> +++ b/sound/virtio/virtio_pcm_msg.c
[ ... ]
> @@ -540,19 +880,52 @@ static void virtsnd_pcm_msg_complete(struct virtio_pcm_msg *msg,
> size_t written_bytes)
> {
> struct virtio_pcm_substream *vss = msg->substream;
> + size_t completed_bytes;
> + u32 latency_bytes;
> +
> + if (vss->direction == SNDRV_PCM_STREAM_CAPTURE &&
> + vss->capture_staging) {
> + virtsnd_pcm_capture_complete(msg, written_bytes);
> + return;
> + }
>
> - /*
> - * hw_ptr always indicates the buffer position of the first I/O message
> - * in the virtqueue. Therefore, on each completion of an I/O message,
> - * the hw_ptr value is unconditionally advanced.
> - */
> + /* Only completed TX requests and valid RX audio advance the pointer. */
> guard(spinlock)(&vss->lock);
> + /* Empty STOP returns retire ownership without inventing captured data. */
> + if (vss->direction == SNDRV_PCM_STREAM_CAPTURE &&
> + (!vss->prepared ||
> + (vss->stopped && written_bytes <= sizeof(*msg->status)))) {
> + msg->length = 0;
> + vss->msg_count--;
> + if (!vss->msg_count)
> + wake_up_all(&vss->msg_empty);
> + return;
> + }
[Severity: High]
As reported by Sashiko on v11 [1], what happens in virtsnd_pcm_msg_complete()
when an in-flight ordinary capture RX buffer completes with
written_bytes > sizeof(*msg->status) after vss->stopped has been set?
On an ordinary capture substream, SNDRV_PCM_TRIGGER_STOP (called during
capture drain in snd_pcm_do_drain_init(), snd_pcm_drop(), or
virtsnd_pcm_sync_stop_locked()) sets vss->stopped = true while vss->prepared
remains true until VIRTIO_SND_R_PCM_RELEASE completes. Unlike staged capture
in virtsnd_pcm_capture_complete(), which ignores all completions when
vss->stopped is true, virtsnd_pcm_msg_complete() only retires stopped capture
completions early if written_bytes <= sizeof(*msg->status).
During SNDRV_PCM_STATE_DRAINING, ALSA stops calling ops->pointer(), so
vss->capture_pointer_seen_total stops advancing while the application drains
already-captured frames via virtsnd_pcm_cp_ack_locked(). If pending RX buffers
complete with audio payloads after STOP, they advance
vss->capture_copied_total until
vss->capture_copied_total - vss->capture_pointer_seen_total >= vss->buffer_bytes
(or fail virtsnd_pcm_capture_status() if completed with a non-OK status),
latching vss->xfer_xrun = true and causing virtsnd_pcm_cp_ack_locked() to
abort capture drain with -EPIPE.
In addition, during virtsnd_pcm_sync_stop_locked(), cancel_work_sync() runs
before VIRTIO_SND_R_PCM_STOP and VIRTIO_SND_R_PCM_RELEASE are sent:
virtsnd_pcm_sync_stop_locked()
cancel_work_sync(&vss->elapsed_period);
rc = virtsnd_pcm_stop_device(vss, epoch);
...
rc = virtsnd_pcm_send_sync(vss, msg); /* VIRTIO_SND_R_PCM_RELEASE */
scoped_guard(spinlock_irqsave, &vss->lock) {
...
vss->prepared = false;
}
If a non-empty RX completion arrives while STOP or RELEASE is in flight and
vss->xfer_xrun is set, virtsnd_pcm_msg_complete() will call
virtsnd_pcm_schedule_period_elapsed(vss) after cancel_work_sync() has already
returned, and will also skip the else-if wake_up_all(&vss->msg_empty) when
vss->msg_count reaches zero. Should virtsnd_pcm_msg_complete() retire all
capture completions whenever !vss->prepared || vss->stopped?
> diff --git a/sound/virtio/virtio_pcm_ops.c b/sound/virtio/virtio_pcm_ops.c
> index ad602b6b6530b..54065662083ed 100644
> --- a/sound/virtio/virtio_pcm_ops.c
> +++ b/sound/virtio/virtio_pcm_ops.c
[ ... ]
> @@ -119,23 +381,42 @@ static int virtsnd_pcm_open(struct snd_pcm_substream *substream)
[ ... ]
> snd_pcm_hw_constraint_integer(substream->runtime,
> SNDRV_PCM_HW_PARAM_PERIODS);
>
> scoped_guard(spinlock_irqsave, &vss->lock)
> vss->stopped |= !!vss->msg_count;
> - vss->suspended = false;
[Severity: Medium]
If vss->suspended = false is removed from virtsnd_pcm_open() and is not
cleared in virtsnd_pcm_hw_params() either, can vss->suspended remain stale
until virtsnd_pcm_prepare_locked()?
When virtsnd_reset_vqs() runs during device reset or freeze/restore, it sets
vss->suspended = true on all substreams (including closed ones), and closing a
substream after SNDRV_PCM_TRIGGER_SUSPEND also leaves vss->suspended = true.
When the substream is subsequently opened via virtsnd_pcm_open() and
configured via virtsnd_pcm_hw_params(), virtsnd_pcm_hw_params() sends
VIRTIO_SND_R_PCM_SET_PARAMS to the device, but vss->suspended remains true.
When virtsnd_pcm_prepare_locked() then runs, it sees suspended == true, which
skips the !virtsnd_pcm_msg_pending_num(vss) queue sanity check and sends a
redundant VIRTIO_SND_R_PCM_SET_PARAMS request to the device. Should
vss->suspended be cleared in virtsnd_pcm_open() or when
virtsnd_pcm_dev_set_params() succeeds in virtsnd_pcm_hw_params()?
[ ... ]
> @@ -320,129 +717,615 @@ static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream)
> + scoped_guard(pcm_stream_lock_irqsave, substream) {
> + guard(spinlock_irqsave)(&vss->lock);
> + if (vss->resetting || vss->reset_epoch != epoch)
> + return -ECANCELED;
> + vss->prepared = false;
> + vss->preparing = true;
> + suspended = vss->suspended;
> + }
> +
> + if (pause_restart) {
> + enum virtio_pcm_pause_restart_state state;
> +
> + scoped_guard(spinlock_irqsave, &vss->lock)
> + state = vss->pause_restart_state;
> + if (state == VIRTSND_PCM_PAUSE_RESTART_RESET_REQUIRED ||
> + virtsnd_pcm_pause_restart_transitioning(state)) {
> + virtsnd_pcm_pause_restart_fail(vss, true, epoch);
> + return -EIO;
> + }
> + }
[Severity: Medium]
If virtsnd_pcm_prepare_locked() fails after setting vss->preparing = true,
what clears vss->preparing?
In snd_pcm_do_prepare(), the core only calls snd_pcm_reset_substream() (which
invokes substream->ops->reset() and clears vss->preparing = false) if
substream->ops->prepare() returns 0. If virtsnd_pcm_prepare_locked() returns
an error on any of the subsequent failure paths (such as the pause_restart
state check, the stopped check, pending messages, virtsnd_pcm_dev_set_params(),
virtsnd_pcm_dev_prepare(), or virtsnd_pcm_capture_refresh_mode_locked()),
vss->preparing remains stuck at true.
Similarly, in virtsnd_pcm_indirect_reset(), virtsnd_pcm_reset(), and
virtsnd_reset_vqs(), if a device reset occurs (setting vss->resetting = true),
virtsnd_pcm_indirect_reset() and virtsnd_pcm_reset() return early at
if (vss->resetting) return 0; before clearing vss->preparing = false, and
virtsnd_reset_vqs() does not clear vss->preparing either. While vss->preparing
is stuck at true, pointer and ACK callbacks remain locked out and return
SNDRV_PCM_POS_XRUN or -EBADFD instead of -EPIPE.
[ ... ]
> +static int virtsnd_pcm_pause_restart_start(struct virtio_pcm_substream *vss,
> + bool resume)
> +{
> + struct virtio_snd_queue *queue = virtsnd_pcm_queue(vss);
> + struct virtio_snd_msg *msg;
> + enum virtio_pcm_pause_restart_state state;
> + bool reset_required;
> + bool invalid_state;
> + int rc = 0;
> + u64 epoch;
> +
> + scoped_guard(spinlock_irqsave, &vss->lock) {
> + if (vss->resetting)
> + return -ECANCELED;
> + epoch = vss->reset_epoch;
> + state = vss->pause_restart_state;
> + reset_required =
> + state == VIRTSND_PCM_PAUSE_RESTART_RESET_REQUIRED ||
> + virtsnd_pcm_pause_restart_transitioning(state);
> + invalid_state =
> + vss->xfer_xrun ||
> + (resume &&\n+ (state != VIRTSND_PCM_PAUSE_RESTART_RELEASED ||
> + !vss->stopped || vss->msg_count ||
> + !vss->runtime_active)) ||
> + (!resume &&\n+ (state != VIRTSND_PCM_PAUSE_RESTART_PREPARED ||
> + vss->stopped));
> + }
> + if (reset_required) {
> + virtsnd_pcm_pause_restart_fail(vss, true, epoch);
> + return -EIO;
> + }
> + if (invalid_state) {
> + virtsnd_pcm_pause_restart_fail(vss, false, epoch);
> + return -EBADFD;
> + }
[Severity: Medium]
In virtsnd_pcm_pause_restart_start(), if vss->xfer_xrun is already latched
(for example via VIRTIO_SND_EVT_PCM_XRUN in virtsnd_pcm_event() while prepared
or before queueing in the second check below), should this return -EPIPE
instead of -EBADFD, matching virtsnd_pcm_start_queue()?
In snd_pcm_do_start(), the ALSA core only transitions the runtime state to
SNDRV_PCM_STATE_XRUN when trigger(SNDRV_PCM_TRIGGER_START) returns -EPIPE.
Returning -EBADFD leaves the substream in SNDRV_PCM_STATE_PREPARED with
vss->stopped = true and vss->xfer_xrun = true, where
virtsnd_pcm_period_elapsed() will not move it to XRUN either because
snd_pcm_running() is false.
[ ... ]
> @@ -521,10 +1426,16 @@ static snd_pcm_uframes_t
> virtsnd_pcm_pb_pointer(struct snd_pcm_substream *substream)
> {
> struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
> + size_t hw_ptr;
> +
> + scoped_guard(spinlock_irqsave, &vss->lock) {
> + if (!vss->prepared || vss->preparing)
> + return SNDRV_PCM_POS_XRUN;
> + hw_ptr = vss->hw_ptr;
> + }
>
> return snd_pcm_indirect_playback_pointer(substream,
> - &vss->pcm_indirect,
> - vss->hw_ptr);
> + &vss->pcm_indirect, hw_ptr);
> }
>
> /**
> @@ -539,10 +1450,62 @@ static snd_pcm_uframes_t
> virtsnd_pcm_cp_pointer(struct snd_pcm_substream *substream)
> {
> struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
> + size_t hw_ptr = 0;
> + u64 total;
> + bool free_running;
> + bool xrun;
> +
> + if (vss->capture_staging) {
> + scoped_guard(spinlock_irqsave, &vss->lock) {
> + xrun = vss->xfer_xrun;
> + if (!xrun &&
> + virtsnd_pcm_capture_refresh_mode_locked(vss,
> + &free_running))
> + xrun = true;
> + if (!xrun && !virtsnd_pcm_capture_counters_valid(vss))
> + xrun = true;
[Severity: Medium]
Unlike virtsnd_pcm_pb_pointer(), the non-staged branch of
virtsnd_pcm_cp_pointer(), and virtsnd_pcm_capture_staged_ack(), the staged
capture branch of virtsnd_pcm_cp_pointer() does not check vss->prepared,
vss->preparing, or vss->suspended.
When virtsnd_reset_vqs() runs during a device reset, it sets
vss->prepared = false, vss->xfer_enabled = false, vss->suspended = true, and
vss->xfer_xrun = false, while leaving vss->runtime_active, vss->buffer_bytes,
vss->capture_boundary, and the staged capture counters intact. Likewise,
virtsnd_pcm_prepare_locked() sets vss->prepared = false and
vss->preparing = true before dropping the PCM stream lock. Because neither
virtsnd_pcm_capture_refresh_mode_locked() nor
virtsnd_pcm_capture_counters_valid() checks vss->prepared, vss->preparing, or
vss->suspended, won't pointer queries on a reset or preparing staged capture
substream return a stale ring offset instead of SNDRV_PCM_POS_XRUN?
[ ... ]
> @@ -571,7 +1536,7 @@ static void virtsnd_pcm_transfer_failed(struct virtio_pcm_substream *vss)
> vss->substream->runtime->stop_operating = true;
> }
>
> -static int virtsnd_pcm_pb_ack(struct snd_pcm_substream *substream)
> +static int virtsnd_pcm_pb_ack_locked(struct snd_pcm_substream *substream)
> {
> struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
> struct virtio_snd_queue *queue = virtsnd_pcm_queue(vss);
> @@ -580,6 +1545,8 @@ static int virtsnd_pcm_pb_ack(struct snd_pcm_substream *substream)
>
> scoped_guard(spinlock_irqsave, &queue->lock) {
> guard(spinlock)(&vss->lock);
> + if (!vss->prepared || vss->preparing)
> + return -EBADFD;
> transfer.indirect = vss->pcm_indirect;
> if (vss->stopped) {
> rc = -EPIPE;
> @@ -606,17 +1573,136 @@ static int virtsnd_pcm_pb_ack(struct snd_pcm_substream *substream)
> return 0;
> }
>
> -static int virtsnd_pcm_cp_ack(struct snd_pcm_substream *substream)
> +/* The PCM lock serializes ACK with prepare's accounting reset. */
> +static int virtsnd_pcm_pb_ack(struct snd_pcm_substream *substream)
> {
> struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
> +
> + if (vss->indirect_reset_observing)
> + return 0;
> + return virtsnd_pcm_pb_ack_locked(substream);
> +}
[Severity: Medium]
In virtsnd_pcm_cp_ack(), the capture path validates runtime->state (returning
-EPIPE for SNDRV_PCM_STATE_XRUN and -EBADFD for states other than PREPARED,
RUNNING, PAUSED, and DRAINING), and virtsnd_pcm_cp_ack_locked() and
virtsnd_pcm_cp_pointer() check vss->xfer_xrun.
In contrast, virtsnd_pcm_pb_ack() does not check runtime->state, and neither
virtsnd_pcm_pb_ack_locked() nor virtsnd_pcm_pb_pointer() checks
vss->xfer_xrun. If a prepared playback stream is dropped via
SNDRV_PCM_IOCTL_DROP (where snd_pcm_drop() transitions runtime->state to
SNDRV_PCM_STATE_SETUP without calling trigger(STOP) because snd_pcm_running()
is false, leaving vss->prepared == true and vss->stopped == false), or if a
later member in a linked prepare sleeps or fails after playback prepare
completes, or if VIRTIO_SND_EVT_PCM_XRUN sets vss->xfer_xrun = true, won't
SNDRV_PCM_IOCTL_SYNC_PTR still call virtsnd_pcm_pb_ack() and enqueue TX
buffers in invalid or XRUN states?
> +
> +static int virtsnd_pcm_capture_staged_ack(struct virtio_pcm_substream *vss,
> + struct snd_pcm_runtime *runtime)
> +{
[ ... ]
> + /* ACK includes any subperiod tail already copied to the ring. */
> + bytes = frames_to_bytes(runtime, diff);
> + outstanding = vss->capture_copied_total -
> + vss->capture_acked_total;
> + if ((!free_running && bytes > vss->capture_ready) ||
> + (free_running &&
> + (bytes > vss->buffer_bytes || bytes > outstanding)) ||
> + check_add_overflow(vss->capture_acked_total, (u64)bytes,
> + &next_acked_total) ||
> + next_acked_total > vss->capture_pointer_seen_total) {
> + vss->xfer_xrun = true;
> + virtsnd_pcm_schedule_period_elapsed(vss);
> + return -EPIPE;
> + }
> +
> + vss->capture_acked_total = next_acked_total;
> + if (!free_running)
> + vss->capture_ready -= bytes;
> + vss->pcm_indirect.appl_ptr = appl_ptr;
> + }
[Severity: Medium]
Can frames_to_bytes(runtime, diff) truncate sub-byte frame deltas here when
capturing 4-bit frames (mono SNDRV_PCM_FORMAT_IMA_ADPCM, where
runtime->frame_bits == 4)?
When diff is odd, frames_to_bytes(runtime, diff) computes (diff * 4) / 8 and
truncates the half-byte, while vss->pcm_indirect.appl_ptr = appl_ptr advances
past the full frame delta. Across multiple odd-frame ACKs (for example via
SNDRV_PCM_IOCTL_SYNC_PTR), the uncounted half-bytes are never added to
vss->capture_acked_total or subtracted from vss->capture_ready, leaking
vss->capture_ready in managed mode until virtsnd_pcm_capture_complete()
rejects a completion with ring_full and triggers a false XRUN.
[ ... ]
> +/* The core holds the stream lock through the matching appl_ptr reset. */
> +static int virtsnd_pcm_reset(struct snd_pcm_substream *substream)
> +{
> + struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + struct virtio_snd_queue *queue = virtsnd_pcm_queue(vss);
> + size_t position;
> + size_t period;
> + int rc;
> +
> + /* Reclaim RX space atomically with the pointer observation. */
> + scoped_guard(spinlock_irqsave, &vss->lock)
> + vss->capture_reset_observing = true;
> + rc = snd_pcm_lib_reset(substream);
> + scoped_guard(spinlock_irqsave, &vss->lock)
> + vss->capture_reset_observing = false;
> + if (rc || !vss->capture_staging)
> + return rc;
> +
> + guard(spinlock_irqsave)(&queue->lock);
> + guard(spinlock)(&vss->lock);
> + if (vss->resetting)
> + return 0;
> + vss->preparing = false;
> + /* The common reset has already handled a pointer-reported XRUN. */
> + if (vss->xfer_xrun)
> + return 0;
[Severity: Medium]
As reported by Sashiko on v8 [2], what happens in virtsnd_pcm_reset() (and
virtsnd_pcm_indirect_reset()) when vss->xfer_xrun is true, either while the
stream is in SNDRV_PCM_STATE_PAUSED / SNDRV_PCM_STATE_PREPARED or when
vss->xfer_xrun becomes true between snd_pcm_lib_reset() and acquiring
vss->lock?
In snd_pcm_lib_reset(), snd_pcm_update_hw_ptr() is only called when
snd_pcm_running(substream) is true. When the substream is paused or prepared
(or if vss->xfer_xrun raced in after snd_pcm_lib_reset()), snd_pcm_lib_reset()
does not observe the XRUN and returns 0, and snd_pcm_reset_substream() then
rebases runtime->control->appl_ptr = runtime->status->hw_ptr. Returning 0
early here leaves vss->pcm_indirect.appl_ptr and the staged capture counters
out of sync with runtime->control->appl_ptr, and in virtsnd_pcm_reset() also
skips scheduling vss->elapsed_period to stop a paused substream on XRUN.
> + if (snd_pcm_running(substream)) {
> + /* Keep completions arriving after the core's pointer observation. */
> + vss->capture_acked_total = vss->capture_pointer_seen_total;
> + vss->capture_ready = vss->capture_free_running ? 0 :
> + vss->capture_copied_total - vss->capture_acked_total;
> + } else {
> + position = frames_to_bytes(runtime, runtime->status->hw_ptr);
> + vss->hw_ptr = position;
> + period = snd_pcm_lib_period_bytes(substream);
> + 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;
> + if (snd_pcm_running(substream) && vss->xfer_enabled && vss->prepared) {
> + rc = virtsnd_pcm_capture_send(vss);
> + if (rc) {
> + vss->xfer_xrun = true;
> + virtsnd_pcm_schedule_period_elapsed(vss);
> + }
> + }
> + /* Complete the matching core pointer rebase before reporting XRUN. */
> + return 0;
> +}
> +
> +/* Keep queued TX ownership while rebasing the application's write cursor. */
> +static int virtsnd_pcm_playback_reset(struct snd_pcm_substream *substream)
> +{
> + struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
> + int rc;
> +
> + vss->indirect_reset_observing = true;
> + rc = snd_pcm_lib_reset(substream);
> + vss->indirect_reset_observing = false;
> + if (rc)
> + return rc;
> + guard(spinlock_irqsave)(&vss->lock);
> + vss->preparing = false;
> + vss->pcm_indirect.appl_ptr = substream->runtime->status->hw_ptr;
> + vss->pcm_indirect.sw_ready = 0;
> + return 0;
> +}
[Severity: High]
Does virtsnd_pcm_playback_reset() leave vss->pcm_indirect and
vss->msgs[i]->length in an inconsistent state when SNDRV_PCM_IOCTL_RESET is
called after playback frames have already been acknowledged?
In virtsnd_pcm_pb_ack_locked(), snd_pcm_indirect_playback_transfer() drains
sw_ready into virtsnd_pcm_msg_send(), which advances pcm_indirect.sw_data,
pcm_indirect.hw_data, and pcm_indirect.hw_ready ahead of pcm_indirect.sw_io
and accumulates bytes into vss->msgs[i]->length. When
virtsnd_pcm_playback_reset() runs, snd_pcm_reset_substream() rebases
runtime->control->appl_ptr back to runtime->status->hw_ptr (matching sw_io),
and virtsnd_pcm_playback_reset() sets pcm_indirect.appl_ptr and clears
sw_ready = 0, but leaves sw_data, hw_data, hw_ready, and vss->msgs[i]->length
untouched:
1. On the next write after RESET, ALSA writes new samples into
runtime->dma_area starting at runtime->control->appl_ptr (sw_io, overwriting
in-flight TX period buffers), while snd_pcm_indirect_playback_transfer()
calls virtsnd_pcm_msg_send() starting from the old sw_data offset, sending
unwritten/stale DMA buffer regions.
2. If a sub-period write occurred before RESET (leaving
0 < msg->length < period_bytes) or when sw_data wraps into an already-queued
period (msg->length == period_bytes), virtsnd_pcm_msg_send() adds n to the
existing msg->length, causing msg->length to exceed period_bytes so the
descriptor is never submitted.
3. Because runtime->control->appl_ptr is rebased back to
runtime->status->hw_ptr while hw_ready bytes remain queued in the TX
virtqueue, completion of an in-flight TX period advances
runtime->status->hw_ptr past runtime->control->appl_ptr and triggers a
false playback XRUN.
4. If SNDRV_PCM_IOCTL_RESET is called while playback is in
SNDRV_PCM_STATE_PAUSED after completing at least one period (vss->hw_ptr > 0),
snd_pcm_lib_reset() sees !snd_pcm_running(substream) and resets
runtime->status->hw_ptr = 0, while vss->hw_ptr, pcm_indirect.hw_io, and
pcm_indirect.sw_io remain at the non-zero paused offset, causing the next
virtsnd_pcm_pb_pointer() call to report a large pointer jump and XRUN.
[1] https://lore.kernel.org/r/20261031020855.2402-10-mynameisjune111@gmail.com
[2] https://lore.kernel.org/r/sashiko-outbox-188791@kernel.org
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261103000403.67106-1-mynameisjune111@gmail.com?part=10
next prev parent reply other threads:[~2026-11-03 0:50 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 0:03 [PATCH v12 00/12] ALSA: virtio: staged capture recovery and PCM RESET synchronization Joonho Lee
2026-11-03 0:03 ` [PATCH v12 01/12] ALSA: virtio: Propagate PCM send failures through ACK Joonho Lee
2026-11-03 0:31 ` sashiko-bot
2026-11-03 0:03 ` [PATCH v12 02/12] ALSA: virtio: Recover PCM state after a failed start Joonho Lee
2026-11-03 0:26 ` sashiko-bot
2026-11-03 0:03 ` [PATCH v12 03/12] ALSA: virtio: Retain queued messages through device reset Joonho Lee
2026-11-03 0:40 ` sashiko-bot
2026-11-03 0:03 ` [PATCH v12 04/12] ALSA: virtio: Keep PCM DMA buffers alive through device ownership Joonho Lee
2026-11-03 0:25 ` sashiko-bot
2026-11-03 0:03 ` [PATCH v12 05/12] ALSA: virtio: Keep control payloads alive through DMA completion Joonho Lee
2026-11-03 0:12 ` sashiko-bot
2026-11-03 0:03 ` [PATCH v12 06/12] ALSA: virtio: wait through signals for PCM teardown Joonho Lee
2026-11-03 0:24 ` sashiko-bot
2026-11-03 0:03 ` [PATCH v12 07/12] ALSA: pcm: mark stop-threshold accesses for capture policy sampling Joonho Lee
2026-11-03 0:09 ` sashiko-bot
2026-11-03 0:03 ` [PATCH v12 08/12] ALSA: pcm: report partial progress under the stream lock Joonho Lee
2026-11-03 0:09 ` sashiko-bot
2026-11-03 0:04 ` [PATCH v12 09/12] ALSA: pcm: add an optional reset operation " Joonho Lee
2026-11-03 0:13 ` sashiko-bot
2026-11-03 0:04 ` [PATCH v12 10/12] ALSA: virtio: add staged capture and serialize PCM lifecycle accounting Joonho Lee
2026-11-03 0:50 ` sashiko-bot [this message]
2026-11-03 0:04 ` [PATCH v12 11/12] ALSA: virtio: prioritize staged capture period work Joonho Lee
2026-11-03 0:15 ` sashiko-bot
2026-11-03 0:04 ` [PATCH v12 12/12] docs: describe virtio-sound capture options Joonho Lee
2026-11-03 0:17 ` sashiko-bot
2026-11-03 0:55 ` Randy Dunlap
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=sashiko-outbox-194389@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=eperezma@redhat.com \
--cc=linux-sound@vger.kernel.org \
--cc=mst@redhat.com \
--cc=mynameisjune111@gmail.com \
--cc=sashiko-reviews@lists.linux.dev \
--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