From 29bb274e94974669acb5186a75538f20df1508b6 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Sat, 6 Feb 2021 21:36:53 +0100
Subject: [PATCH] ALSA: pcm: Call sync_stop at disconnection
Git-commit: 29bb274e94974669acb5186a75538f20df1508b6
Patch-mainline: v5.12-rc1
References: git-fixes
The PCM core should perform the sync for the pending stop operations
at disconnection. Otherwise it may lead to unexpected access.
Currently the old user of sync_stop, USB-audio driver, has its own
sync, so this isn't needed, but it's better to guarantee the sync in
the PCM core level.
This patch adds the missing sync_stop call at PCM disconnection
callback. It also assures the IRQ sync if it's specified in the
card. snd_pcm_sync_stop() is slightly modified to be called also for
any PCM substream object now.
Fixes: 1e850beea278 ("ALSA: pcm: Add the support for sync-stop operation")
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20210206203656.15959-2-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
sound/core/init.c | 4 ++++
sound/core/pcm.c | 4 ++++
sound/core/pcm_local.h | 1 +
sound/core/pcm_native.c | 16 ++++++++--------
4 files changed, 17 insertions(+), 8 deletions(-)
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -14,6 +14,7 @@
#include <linux/ctype.h>
#include <linux/pm.h>
#include <linux/completion.h>
+#include <linux/interrupt.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -414,6 +415,9 @@ int snd_card_disconnect(struct snd_card
/* notify all devices that we are disconnected */
snd_device_disconnect_all(card);
+ if (card->sync_irq > 0)
+ synchronize_irq(card->sync_irq);
+
snd_info_card_disconnect(card);
if (card->registered) {
device_del(&card->card_dev);
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1111,6 +1111,10 @@ static int snd_pcm_dev_disconnect(struct
}
}
+ for (cidx = 0; cidx < 2; cidx++)
+ for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
+ snd_pcm_sync_stop(substream, false);
+
pcm_call_notify(pcm, n_disconnect);
for (cidx = 0; cidx < 2; cidx++) {
snd_unregister_device(&pcm->streams[cidx].dev);
--- a/sound/core/pcm_local.h
+++ b/sound/core/pcm_local.h
@@ -66,6 +66,7 @@ static inline void snd_pcm_timer_done(st
void __snd_pcm_xrun(struct snd_pcm_substream *substream);
void snd_pcm_group_init(struct snd_pcm_group *group);
+void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq);
#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -580,13 +580,13 @@ static inline void snd_pcm_timer_notify(
#endif
}
-static void snd_pcm_sync_stop(struct snd_pcm_substream *substream)
+void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq)
{
- if (substream->runtime->stop_operating) {
+ if (substream->runtime && substream->runtime->stop_operating) {
substream->runtime->stop_operating = false;
- if (substream->ops->sync_stop)
+ if (substream->ops && substream->ops->sync_stop)
substream->ops->sync_stop(substream);
- else if (substream->pcm->card->sync_irq > 0)
+ else if (sync_irq && substream->pcm->card->sync_irq > 0)
synchronize_irq(substream->pcm->card->sync_irq);
}
}
@@ -683,7 +683,7 @@ static int snd_pcm_hw_params(struct snd_
if (atomic_read(&substream->mmap_count))
return -EBADFD;
- snd_pcm_sync_stop(substream);
+ snd_pcm_sync_stop(substream, true);
params->rmask = ~0U;
err = snd_pcm_hw_refine(substream, params);
@@ -806,7 +806,7 @@ static int do_hw_free(struct snd_pcm_sub
{
int result = 0;
- snd_pcm_sync_stop(substream);
+ snd_pcm_sync_stop(substream, true);
if (substream->ops->hw_free)
result = substream->ops->hw_free(substream);
if (substream->managed_buffer_alloc)
@@ -1633,7 +1633,7 @@ static void snd_pcm_post_resume(struct s
snd_pcm_trigger_tstamp(substream);
runtime->status->state = runtime->status->suspended_state;
snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
- snd_pcm_sync_stop(substream);
+ snd_pcm_sync_stop(substream, true);
}
static const struct action_ops snd_pcm_action_resume = {
@@ -1754,7 +1754,7 @@ static int snd_pcm_pre_prepare(struct sn
static int snd_pcm_do_prepare(struct snd_pcm_substream *substream, int state)
{
int err;
- snd_pcm_sync_stop(substream);
+ snd_pcm_sync_stop(substream, true);
err = substream->ops->prepare(substream);
if (err < 0)
return err;