| 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: ALSA: pcm: Call sync_stop at disconnection |
| |
| From: Takashi Iwai <tiwai@suse.de> |
| |
| commit 29bb274e94974669acb5186a75538f20df1508b6 upstream. |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| 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> |
| @@ -416,6 +417,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 |
| @@ -63,6 +63,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); |
| |
| #ifdef CONFIG_SND_DMA_SGBUF |
| struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, |
| --- a/sound/core/pcm_native.c |
| +++ b/sound/core/pcm_native.c |
| @@ -583,13 +583,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); |
| } |
| } |
| @@ -686,7 +686,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); |
| @@ -809,7 +809,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) |
| @@ -1736,7 +1736,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 = { |
| @@ -1866,7 +1866,7 @@ static int snd_pcm_do_prepare(struct snd |
| snd_pcm_state_t state) |
| { |
| int err; |
| - snd_pcm_sync_stop(substream); |
| + snd_pcm_sync_stop(substream, true); |
| err = substream->ops->prepare(substream); |
| if (err < 0) |
| return err; |