Linux virtualization list
 help / color / mirror / Atom feed
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 v8 08/14] ALSA: pcm: stop paused streams before synchronizing teardown
Date: Thu, 29 Oct 2026 06:04:46 +0000	[thread overview]
Message-ID: <sashiko-outbox-188788@kernel.org> (raw)
In-Reply-To: <20261029054354.2931-9-mynameisjune111@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] ALSA: pcm: unbalanced STOP trigger from PAUSED in snd_pcm_do_stop()
--

commit 1209424a81e0273da5db98a8f56a0fd0d7015011
Author: Joonho Lee <mynameisjune111@gmail.com>

ALSA: pcm: stop paused streams before synchronizing teardown

This commit updates snd_pcm_do_stop() to issue the SNDRV_PCM_TRIGGER_STOP
trigger and set stop_operating when a substream is in SNDRV_PCM_STATE_PAUSED,
in addition to when it is running.

> diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
> index a2ea44c3c1d29..2b18c5f5801f9 100644
> --- a/sound/core/pcm_native.c
> +++ b/sound/core/pcm_native.c
> @@ -1607,7 +1607,8 @@ static int snd_pcm_do_stop(struct snd_pcm_substream *substream,
>  			   snd_pcm_state_t state)
>  {
>  	if (substream->runtime->trigger_master == substream &&
> -	    snd_pcm_running(substream)) {
> +	    (snd_pcm_running(substream) ||
> +	     substream->runtime->state == SNDRV_PCM_STATE_PAUSED)) {
>  		substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
>  		substream->runtime->stop_operating = true;
>  	}

[Severity: High]
Can calling substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP)
directly while in SNDRV_PCM_STATE_PAUSED cause regressions in drivers that
expect start-class triggers (START, RESUME, PAUSE_RELEASE) and stop-class
triggers (STOP, SUSPEND, PAUSE_PUSH) to be strictly paired?

When a substream enters SNDRV_PCM_STATE_PAUSED via snd_pcm_do_pause(), the
driver has already received SNDRV_PCM_TRIGGER_PAUSE_PUSH. Reaching
snd_pcm_do_stop() while runtime->state is still SNDRV_PCM_STATE_PAUSED
delivers SNDRV_PCM_TRIGGER_STOP without an intervening
SNDRV_PCM_TRIGGER_PAUSE_RELEASE.

This path can be reached from userspace when:

1. __snd_pcm_xrun() -> snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN) runs on
   a paused stream without calling snd_pcm_pause(substream, false), such as
   when ops->ack() returns -EPIPE in pcm_lib_apply_appl_ptr() during write,
   read, rewind, forward, or SNDRV_PCM_IOCTL_SYNC_PTR, when avail reaches
   runtime->stop_threshold in snd_pcm_update_state(), or via a direct driver
   snd_pcm_stop() call.
2. snd_pcm_pause(substream, false) in snd_pcm_drop() or snd_pcm_prepare()
   fails (its return value is ignored), for example if the driver's
   SNDRV_PCM_TRIGGER_PAUSE_RELEASE trigger returns an error or if a linked
   substream is not in SNDRV_PCM_STATE_PAUSED so snd_pcm_pre_pause() returns
   -EBADFD.
3. snd_pcm_drop(), snd_pcm_prepare(), or snd_pcm_stop() is called on a
   non-paused substream that is linked to a paused substream.

Several in-tree drivers appear to rely on never receiving
SNDRV_PCM_TRIGGER_STOP after SNDRV_PCM_TRIGGER_PAUSE_PUSH without an
intervening release:

1. Clock enable_count underflow in sound/soc/qcom/lpass-cpu.c
   (lpass_cpu_daiops_trigger()) and sound/soc/spear/spdif_in.c
   (spdif_in_trigger()):

   sound/soc/qcom/lpass-cpu.c:lpass_cpu_daiops_trigger() {
       ...
       case SNDRV_PCM_TRIGGER_STOP:
       case SNDRV_PCM_TRIGGER_SUSPEND:
       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
           ...
           clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
           break;
   }

   Receiving STOP while PAUSED calls clk_disable() a second time for a single
   clk_enable().

2. Runtime PM usage count underflow in sound/soc/samsung/i2s.c
   (i2s_trigger()):

   sound/soc/samsung/i2s.c:i2s_trigger() {
       ...
       case SNDRV_PCM_TRIGGER_STOP:
       case SNDRV_PCM_TRIGGER_SUSPEND:
       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
           ...
           pm_runtime_put(dai->dev);
           break;
   }

   Receiving STOP after PAUSE_PUSH calls pm_runtime_put(dai->dev) twice.

3. Reference and active counter underflows or premature shared hardware
   shutdown in sound/soc/stm/stm32_i2s.c (stm32_i2s_trigger() decrementing
   i2s->refcount), sound/soc/rockchip/rockchip_i2s_tdm.c
   (rockchip_snd_txrxctrl() decrementing i2s_tdm->refcount),
   sound/soc/atmel/atmel-i2s.c (atmel_i2s_trigger() decrementing
   dev->clk_use_no), sound/soc/dwc/dwc-i2s.c (dw_i2s_trigger() decrementing
   dev->active), sound/soc/ti/omap-mcbsp.c (omap_mcbsp_dai_trigger()
   decrementing mcbsp->active), sound/soc/pxa/mmp-sspa.c
   (mmp_sspa_trigger()), sound/soc/bcm/cygnus-ssp.c (cygnus_ssp_trigger()),
   and sound/soc/fsl/imx-pcm-fiq.c (snd_imx_pcm_trigger() calling
   disable_fiq(imx_pcm_fiq) twice):

   sound/soc/stm/stm32_i2s.c:stm32_i2s_trigger() {
       ...
       case SNDRV_PCM_TRIGGER_STOP:
       case SNDRV_PCM_TRIGGER_SUSPEND:
       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
           ...
           scoped_guard(spinlock, &i2s->lock_fd) {
               i2s->refcount--;
   }

4. SOF IPC4 pipeline state leak in sound/soc/sof/ipc4-pcm.c:

   sound/soc/sof/ipc4-pcm.c:sof_ipc4_add_pipeline_to_trigger_list() {
       ...
       case SOF_IPC4_PIPE_RESET:
           /* RESET if the pipeline is neither running nor paused */
           if (!spipe->started_count && !spipe->paused_count)
               sof_ipc4_add_pipeline_by_priority(trigger_list, pipe_widget, pipe_priority,
                                                 true);
           break;
       case SOF_IPC4_PIPE_PAUSED:
       case SOF_IPC4_PIPE_EOS:
           if (spipe->paused_count == (spipe->started_count - 1))
               sof_ipc4_add_pipeline_by_priority(trigger_list, pipe_widget, pipe_priority,
                                                 true);
           break;
   }

   When START is followed by PAUSE_PUSH, spipe->started_count is 1 and
   spipe->paused_count is 1. If SNDRV_PCM_TRIGGER_STOP is then issued while
   PAUSED, sof_ipc4_pcm_trigger() requests SOF_IPC4_PIPE_PAUSED, the check
   spipe->paused_count == (spipe->started_count - 1) evaluates to false, and
   neither started_count nor paused_count is decremented (and
   sof_ipc4_update_pipeline_state() only decrements paused_count on
   SNDRV_PCM_TRIGGER_PAUSE_RELEASE). Subsequent sof_ipc4_pcm_hw_free() then
   skips SOF_IPC4_PIPE_RESET because started_count and paused_count remain
   non-zero.

5. Stuck hardware pause bits in sound/pci/cmipci.c (snd_cmipci_pcm_trigger()),
   sound/pci/oxygen/oxygen_pcm.c (oxygen_trigger()), sound/pci/als300.c
   (snd_als300_trigger()), sound/pci/ens1370.c (snd_ensoniq_trigger()), and
   sound/pci/ice1712/ice1712.c (snd_ice1712_pcm_trigger()):

   sound/pci/cmipci.c:snd_cmipci_pcm_trigger() {
       ...
       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
       case SNDRV_PCM_TRIGGER_SUSPEND:
           cm->ctrl |= pause;
           snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
           break;
       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
       case SNDRV_PCM_TRIGGER_RESUME:
           cm->ctrl &= ~pause;
           snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
           break;
   }

   In these drivers, the pause bit is only cleared on PAUSE_RELEASE or RESUME,
   not by SNDRV_PCM_TRIGGER_STOP or prepare().

6. Duplicate VIRTIO_SND_R_PCM_STOP in sound/virtio/virtio_pcm_ops.c
   (virtsnd_pcm_trigger()):

   sound/virtio/virtio_pcm_ops.c:virtsnd_pcm_trigger() {
       ...
       case SNDRV_PCM_TRIGGER_STOP:
       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
           scoped_guard(spinlock_irqsave, &vss->lock) {
               if (command != SNDRV_PCM_TRIGGER_PAUSE_PUSH)
                   vss->stopped = true;
               vss->xfer_enabled = false;
           }

           msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_STOP,
                                           GFP_KERNEL);
           if (!msg)
               return -ENOMEM;

           return virtsnd_ctl_msg_send_sync(snd, msg);
   }

   Because SNDRV_PCM_TRIGGER_PAUSE_PUSH already sends VIRTIO_SND_R_PCM_STOP
   to the device, triggering SNDRV_PCM_TRIGGER_STOP from
   SNDRV_PCM_STATE_PAUSED sends a second VIRTIO_SND_R_PCM_STOP while the
   device stream is already stopped.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029054354.2931-1-mynameisjune111@gmail.com?part=8

  reply	other threads:[~2026-10-29  6:04 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29  5:43 [PATCH v8 00/14] ALSA: virtio: staged capture recovery and PCM RESET synchronization Joonho Lee
2026-10-29  5:43 ` [PATCH v8 01/14] ALSA: virtio: Propagate PCM send failures through ACK Joonho Lee
2026-10-29  6:06   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 02/14] ALSA: virtio: Recover PCM state after a failed start Joonho Lee
2026-10-29  6:10   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 03/14] ALSA: virtio: Retain queued messages through device reset Joonho Lee
2026-10-29  6:14   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 04/14] ALSA: virtio: Keep PCM DMA buffers alive through device ownership Joonho Lee
2026-10-29  6:07   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 05/14] ALSA: virtio: Keep control payloads alive through DMA completion Joonho Lee
2026-10-29  5:54   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 06/14] ALSA: virtio: wait through signals for PCM teardown Joonho Lee
2026-10-29  6:00   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 07/14] ALSA: pcm: mark stop-threshold accesses for capture policy sampling Joonho Lee
2026-10-29  5:48   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 08/14] ALSA: pcm: stop paused streams before synchronizing teardown Joonho Lee
2026-10-29  6:04   ` sashiko-bot [this message]
2026-10-29  5:43 ` [PATCH v8 09/14] ALSA: virtio: add staged capture accounting and lifecycle recovery Joonho Lee
2026-10-29  6:23   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 10/14] ALSA: virtio: prioritize staged capture period work Joonho Lee
2026-10-29  5:48   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 11/14] docs: describe virtio-sound capture options Joonho Lee
2026-10-29  5:47   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 12/14] ALSA: pcm: add an optional reset operation under the stream lock Joonho Lee
2026-10-29  5:52   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 13/14] ALSA: virtio: synchronize staged capture reset with PCM positions Joonho Lee
2026-10-29  6:05   ` sashiko-bot
2026-10-29  5:43 ` [PATCH v8 14/14] ALSA: virtio: synchronize indirect capture positions on reset Joonho Lee
2026-10-29  6:06   ` sashiko-bot

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-188788@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