ASoc: msm: Fix the kernel panic issue in dapm
- Widget list in dapm is getting corrupted during concurrent
use cases where dapm_power_widget is accessed from core,
machine driver, codec driver and soc-dsp. This corruption
is resulting in kernel crash in dapm.
- Fix the issue by adding protection in dapm_power_widgets
API.
CRs-fixed: 351250
Change-Id: Ia977770b2c4081353918a40919aafd468bf3c4ed
Signed-off-by: Jayasena Sangaraboina <jsanga@codeaurora.org>
diff --git a/include/sound/soc.h b/include/sound/soc.h
index f51b006..12a8fd5 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -814,6 +814,7 @@
struct list_head list;
struct mutex mutex;
struct mutex dapm_mutex;
+ struct mutex dapm_power_mutex;
struct mutex dsp_mutex;
spinlock_t dsp_spinlock;
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index abfadb8..44fab3f 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -254,6 +254,8 @@
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
+ mutex_lock(&dapm->codec->mutex);
+
pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
if (msm8960_spk_control == MSM8960_SPK_ON) {
snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
@@ -268,6 +270,7 @@
}
snd_soc_dapm_sync(dapm);
+ mutex_unlock(&dapm->codec->mutex);
}
static int msm8960_get_spk(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index f47b0d3..4192609 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -3811,6 +3811,7 @@
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
+ mutex_init(&card->dapm_power_mutex);
mutex_init(&card->dsp_mutex);
spin_lock_init(&card->dsp_spinlock);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 265df9e..2284f19 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1306,15 +1306,24 @@
struct snd_soc_dapm_context *cur_dapm = NULL;
int ret, i;
int *sort;
+ int nWidgets;
if (power_up)
sort = dapm_up_seq;
else
sort = dapm_down_seq;
+ nWidgets = ARRAY_SIZE(dapm_up_seq);
+
list_for_each_entry_safe(w, n, list, power_list) {
ret = 0;
+ if (!w->name)
+ continue;
+
+ if (!((w->id >= 0) && (w->id < nWidgets)))
+ continue;
+
/* Do we need to apply any queued changes? */
if (sort[w->id] != cur_sort || w->reg != cur_reg ||
w->dapm != cur_dapm || w->subseq != cur_subseq) {
@@ -1500,6 +1509,8 @@
trace_snd_soc_dapm_start(card);
+ mutex_lock(&card->dapm_power_mutex);
+
list_for_each_entry(d, &card->dapm_list, list)
if (d->n_widgets || d->codec == NULL)
d->dev_power = 0;
@@ -1609,6 +1620,8 @@
"DAPM sequencing finished, waiting %dms\n", card->pop_time);
pop_wait(card->pop_time);
+ mutex_unlock(&card->dapm_power_mutex);
+
trace_snd_soc_dapm_done(card);
return 0;