ASoC: Fix component lists locking

Any access to the component_list, codec_list and platform_list needs to be
properly locked by the client_mutex. Otherwise undefined behavior can occur
if the list is modified in one thread and concurrently accessed from another
thread.

This patch adds the missing locking to the debugfs file handlers that
display the registered components, as well as the various components
unregister functions.

Furthermore the client_lock is now held for the whole
snd_soc_instantiate_card() sequence to make sure that component removal does
not race against the card registration.

Reported-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Lars-Peter Clausen 2015-03-07 19:34:03 +01:00 committed by Mark Brown
parent c517d838eb
commit 34e81ab455

View File

@ -347,6 +347,8 @@ static ssize_t codec_list_read_file(struct file *file, char __user *user_buf,
if (!buf)
return -ENOMEM;
mutex_lock(&client_mutex);
list_for_each_entry(codec, &codec_list, list) {
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
codec->component.name);
@ -358,6 +360,8 @@ static ssize_t codec_list_read_file(struct file *file, char __user *user_buf,
}
}
mutex_unlock(&client_mutex);
if (ret >= 0)
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
@ -382,6 +386,8 @@ static ssize_t dai_list_read_file(struct file *file, char __user *user_buf,
if (!buf)
return -ENOMEM;
mutex_lock(&client_mutex);
list_for_each_entry(component, &component_list, list) {
list_for_each_entry(dai, &component->dai_list, list) {
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
@ -395,6 +401,8 @@ static ssize_t dai_list_read_file(struct file *file, char __user *user_buf,
}
}
mutex_unlock(&client_mutex);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
@ -418,6 +426,8 @@ static ssize_t platform_list_read_file(struct file *file,
if (!buf)
return -ENOMEM;
mutex_lock(&client_mutex);
list_for_each_entry(platform, &platform_list, list) {
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
platform->component.name);
@ -429,6 +439,8 @@ static ssize_t platform_list_read_file(struct file *file,
}
}
mutex_unlock(&client_mutex);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
@ -836,6 +848,8 @@ static struct snd_soc_component *soc_find_component(
{
struct snd_soc_component *component;
lockdep_assert_held(&client_mutex);
list_for_each_entry(component, &component_list, list) {
if (of_node) {
if (component->dev->of_node == of_node)
@ -854,6 +868,8 @@ static struct snd_soc_dai *snd_soc_find_dai(
struct snd_soc_component *component;
struct snd_soc_dai *dai;
lockdep_assert_held(&client_mutex);
/* Find CPU DAI from registered DAIs*/
list_for_each_entry(component, &component_list, list) {
if (dlc->of_node && component->dev->of_node != dlc->of_node)
@ -1508,6 +1524,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
struct snd_soc_codec *codec;
int ret, i, order;
mutex_lock(&client_mutex);
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
/* bind DAIs */
@ -1662,6 +1679,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
card->instantiated = 1;
snd_soc_dapm_sync(&card->dapm);
mutex_unlock(&card->mutex);
mutex_unlock(&client_mutex);
return 0;
@ -1680,6 +1698,7 @@ card_probe_error:
base_error:
mutex_unlock(&card->mutex);
mutex_unlock(&client_mutex);
return ret;
}
@ -2713,13 +2732,6 @@ static void snd_soc_component_del_unlocked(struct snd_soc_component *component)
list_del(&component->list);
}
static void snd_soc_component_del(struct snd_soc_component *component)
{
mutex_lock(&client_mutex);
snd_soc_component_del_unlocked(component);
mutex_unlock(&client_mutex);
}
int snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *cmpnt_drv,
struct snd_soc_dai_driver *dai_drv,
@ -2767,14 +2779,17 @@ void snd_soc_unregister_component(struct device *dev)
{
struct snd_soc_component *cmpnt;
mutex_lock(&client_mutex);
list_for_each_entry(cmpnt, &component_list, list) {
if (dev == cmpnt->dev && cmpnt->registered_as_component)
goto found;
}
mutex_unlock(&client_mutex);
return;
found:
snd_soc_component_del(cmpnt);
snd_soc_component_del_unlocked(cmpnt);
mutex_unlock(&client_mutex);
snd_soc_component_cleanup(cmpnt);
kfree(cmpnt);
}
@ -2882,10 +2897,14 @@ struct snd_soc_platform *snd_soc_lookup_platform(struct device *dev)
{
struct snd_soc_platform *platform;
mutex_lock(&client_mutex);
list_for_each_entry(platform, &platform_list, list) {
if (dev == platform->dev)
if (dev == platform->dev) {
mutex_unlock(&client_mutex);
return platform;
}
}
mutex_unlock(&client_mutex);
return NULL;
}
@ -3090,15 +3109,15 @@ void snd_soc_unregister_codec(struct device *dev)
{
struct snd_soc_codec *codec;
mutex_lock(&client_mutex);
list_for_each_entry(codec, &codec_list, list) {
if (dev == codec->dev)
goto found;
}
mutex_unlock(&client_mutex);
return;
found:
mutex_lock(&client_mutex);
list_del(&codec->list);
snd_soc_component_del_unlocked(&codec->component);
mutex_unlock(&client_mutex);