Commit 3994e0b1 authored by Vaibhav Agarwal's avatar Vaibhav Agarwal Committed by Greg Kroah-Hartman

greybus: audio: use variable 'is_connected' to maintain module state

there is race condition between _disconnect() request &
stop_trigger() in case of abrupt module removal.
And sometimes this can lead to deadlock while acquiring
codec_info->lock.
To avoid such situation, atomic variable is used to maintain
codec connected state.
During dai operations (trigger, shutdown, etc.), 'is_connected'
variable is validated to avoid unnecessary lock acquire in
case module already removed.
Signed-off-by: default avatarVaibhav Agarwal <vaibhav.agarwal@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 796fad44
...@@ -44,6 +44,9 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, ...@@ -44,6 +44,9 @@ static int gbcodec_startup(struct snd_pcm_substream *substream,
struct gbaudio_dai *gb_dai; struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
/* find the dai */ /* find the dai */
mutex_lock(&gb->lock); mutex_lock(&gb->lock);
gb_dai = gbaudio_find_dai(gb, -1, dai->name); gb_dai = gbaudio_find_dai(gb, -1, dai->name);
...@@ -77,6 +80,9 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, ...@@ -77,6 +80,9 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream,
struct gbaudio_dai *gb_dai; struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected))
return;
/* find the dai */ /* find the dai */
mutex_lock(&gb->lock); mutex_lock(&gb->lock);
gb_dai = gbaudio_find_dai(gb, -1, dai->name); gb_dai = gbaudio_find_dai(gb, -1, dai->name);
...@@ -129,6 +135,9 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, ...@@ -129,6 +135,9 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream,
struct gbaudio_dai *gb_dai; struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
/* find the dai */ /* find the dai */
mutex_lock(&gb->lock); mutex_lock(&gb->lock);
gb_dai = gbaudio_find_dai(gb, -1, dai->name); gb_dai = gbaudio_find_dai(gb, -1, dai->name);
...@@ -204,6 +213,9 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, ...@@ -204,6 +213,9 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream,
struct gbaudio_dai *gb_dai; struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
/* find the dai */ /* find the dai */
mutex_lock(&gb->lock); mutex_lock(&gb->lock);
gb_dai = gbaudio_find_dai(gb, -1, dai->name); gb_dai = gbaudio_find_dai(gb, -1, dai->name);
...@@ -278,6 +290,9 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -278,6 +290,9 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd,
struct gbaudio_dai *gb_dai; struct gbaudio_dai *gb_dai;
struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
/* find the dai */ /* find the dai */
mutex_lock(&gb->lock); mutex_lock(&gb->lock);
gb_dai = gbaudio_find_dai(gb, -1, dai->name); gb_dai = gbaudio_find_dai(gb, -1, dai->name);
...@@ -776,6 +791,7 @@ static int gb_audio_probe(struct gb_bundle *bundle, ...@@ -776,6 +791,7 @@ static int gb_audio_probe(struct gb_bundle *bundle,
desc.devices = 0x2; /* todo */ desc.devices = 0x2; /* todo */
gbcodec->manager_id = gb_audio_manager_add(&desc); gbcodec->manager_id = gb_audio_manager_add(&desc);
atomic_set(&gbcodec->is_connected, 1);
list_add(&gbcodec->list, &gb_codec_list); list_add(&gbcodec->list, &gb_codec_list);
dev_dbg(dev, "Add GB Audio device:%s\n", gbcodec->name); dev_dbg(dev, "Add GB Audio device:%s\n", gbcodec->name);
mutex_unlock(&gb_codec_list_lock); mutex_unlock(&gb_codec_list_lock);
...@@ -809,6 +825,7 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) ...@@ -809,6 +825,7 @@ static void gb_audio_disconnect(struct gb_bundle *bundle)
struct gbaudio_dai *dai, *_dai; struct gbaudio_dai *dai, *_dai;
mutex_lock(&gb_codec_list_lock); mutex_lock(&gb_codec_list_lock);
atomic_set(&gbcodec->is_connected, 0);
list_del(&gbcodec->list); list_del(&gbcodec->list);
/* inform uevent to above layers */ /* inform uevent to above layers */
gb_audio_manager_remove(gbcodec->manager_id); gb_audio_manager_remove(gbcodec->manager_id);
......
...@@ -108,6 +108,15 @@ struct gbaudio_codec_info { ...@@ -108,6 +108,15 @@ struct gbaudio_codec_info {
int manager_id; int manager_id;
char name[NAME_SIZE]; char name[NAME_SIZE];
/*
* there can be a rece condition between gb_audio_disconnect()
* and dai->trigger from above ASoC layer.
* To avoid any deadlock over codec_info->lock, atomic variable
* is used.
*/
atomic_t is_connected;
struct mutex lock;
/* soc related data */ /* soc related data */
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
struct device *dev; struct device *dev;
...@@ -139,7 +148,6 @@ struct gbaudio_codec_info { ...@@ -139,7 +148,6 @@ struct gbaudio_codec_info {
struct list_head widget_list; struct list_head widget_list;
struct list_head codec_ctl_list; struct list_head codec_ctl_list;
struct list_head widget_ctl_list; struct list_head widget_ctl_list;
struct mutex lock;
}; };
struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec,
......
...@@ -140,6 +140,9 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, ...@@ -140,6 +140,9 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; info = (struct gb_audio_ctl_elem_info *)data->info;
...@@ -187,6 +190,9 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, ...@@ -187,6 +190,9 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; info = (struct gb_audio_ctl_elem_info *)data->info;
...@@ -281,6 +287,9 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, ...@@ -281,6 +287,9 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = widget->codec; struct snd_soc_codec *codec = widget->codec;
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; info = (struct gb_audio_ctl_elem_info *)data->info;
...@@ -315,6 +324,9 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, ...@@ -315,6 +324,9 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = widget->codec; struct snd_soc_codec *codec = widget->codec;
struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
if (!atomic_read(&gb->is_connected))
return -ENODEV;
data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
info = (struct gb_audio_ctl_elem_info *)data->info; info = (struct gb_audio_ctl_elem_info *)data->info;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment