diff options
author | Takashi Iwai <tiwai@suse.de> | 2017-08-25 06:04:07 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2017-09-27 13:08:52 -0400 |
commit | 5d61f0ba6524dcbad198126e5793157c8afdea91 (patch) | |
tree | 279819f50b58c4c38efb13f3a41414354ecdb05d /sound/soc/soc-pcm.c | |
parent | 2bd6bf03f4c1c59381d62c61d03f6cc3fe71f66e (diff) |
ASoC: pcm: Sync delayed work before releasing resources
When ASoC driver is unbound dynamically during its operation (i.e. a
kind of hot-unplug), we may hit Oops due to the resource access after
the release by a delayed work, something like:
Unable to handle kernel paging request at virtual address dead000000000220
....
PC is at soc_dapm_dai_stream_event.isra.14+0x20/0xd0
LR is at snd_soc_dapm_stream_event+0x74/0xa8
....
[<ffff000008715610>] soc_dapm_dai_stream_event.isra.14+0x20/0xd0
[<ffff00000871989c>] snd_soc_dapm_stream_event+0x74/0xa8
[<ffff00000871b23c>] close_delayed_work+0x3c/0x50
[<ffff0000080bbd6c>] process_one_work+0x1ac/0x318
[<ffff0000080bbf20>] worker_thread+0x48/0x420
[<ffff0000080c201c>] kthread+0xfc/0x128
[<ffff0000080842f0>] ret_from_fork+0x10/0x18
For fixing the race, this patch adds a sync-point in pcm private_free
callback to finish the delayed work before actually releasing the
resources.
Reported-by: Hiep Cao Minh <cm-hiep@jinso.co.jp>
Reported-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tested-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/soc-pcm.c')
-rw-r--r-- | sound/soc/soc-pcm.c | 13 |
1 files changed, 12 insertions, 1 deletions
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 94b88b897c3b..c0f0b09cb433 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c | |||
@@ -2632,6 +2632,17 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) | |||
2632 | return ret; | 2632 | return ret; |
2633 | } | 2633 | } |
2634 | 2634 | ||
2635 | static void soc_pcm_private_free(struct snd_pcm *pcm) | ||
2636 | { | ||
2637 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | ||
2638 | struct snd_soc_platform *platform = rtd->platform; | ||
2639 | |||
2640 | /* need to sync the delayed work before releasing resources */ | ||
2641 | flush_delayed_work(&rtd->delayed_work); | ||
2642 | if (platform->driver->pcm_free) | ||
2643 | platform->driver->pcm_free(pcm); | ||
2644 | } | ||
2645 | |||
2635 | /* create a new pcm */ | 2646 | /* create a new pcm */ |
2636 | int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) | 2647 | int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) |
2637 | { | 2648 | { |
@@ -2757,7 +2768,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) | |||
2757 | } | 2768 | } |
2758 | } | 2769 | } |
2759 | 2770 | ||
2760 | pcm->private_free = platform->driver->pcm_free; | 2771 | pcm->private_free = soc_pcm_private_free; |
2761 | out: | 2772 | out: |
2762 | dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", | 2773 | dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", |
2763 | (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, | 2774 | (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, |