From: Vaibhav Agarwal Date: Thu, 28 Jan 2016 15:45:40 +0000 (+0530) Subject: greybus: audio: use variable 'is_connected' to maintain module state X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=3994e0b139c709047cdeb44b6c28cfb39f89f3f2;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git 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: Vaibhav Agarwal Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index ad28c10fa154..c05ab4fb8754 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -44,6 +44,9 @@ static int gbcodec_startup(struct snd_pcm_substream *substream, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -77,6 +80,9 @@ static void gbcodec_shutdown(struct snd_pcm_substream *substream, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -129,6 +135,9 @@ static int gbcodec_hw_params(struct snd_pcm_substream *substream, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -204,6 +213,9 @@ static int gbcodec_prepare(struct snd_pcm_substream *substream, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -278,6 +290,9 @@ static int gbcodec_trigger(struct snd_pcm_substream *substream, int cmd, struct gbaudio_dai *gb_dai; struct gbaudio_codec_info *gb = dev_get_drvdata(dai->dev); + if (!atomic_read(&gb->is_connected)) + return -ENODEV; + /* find the dai */ mutex_lock(&gb->lock); gb_dai = gbaudio_find_dai(gb, -1, dai->name); @@ -776,6 +791,7 @@ static int gb_audio_probe(struct gb_bundle *bundle, desc.devices = 0x2; /* todo */ gbcodec->manager_id = gb_audio_manager_add(&desc); + atomic_set(&gbcodec->is_connected, 1); list_add(&gbcodec->list, &gb_codec_list); dev_dbg(dev, "Add GB Audio device:%s\n", gbcodec->name); mutex_unlock(&gb_codec_list_lock); @@ -809,6 +825,7 @@ static void gb_audio_disconnect(struct gb_bundle *bundle) struct gbaudio_dai *dai, *_dai; mutex_lock(&gb_codec_list_lock); + atomic_set(&gbcodec->is_connected, 0); list_del(&gbcodec->list); /* inform uevent to above layers */ gb_audio_manager_remove(gbcodec->manager_id); diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h index 56110913b70e..4c19bd884488 100644 --- a/drivers/staging/greybus/audio_codec.h +++ b/drivers/staging/greybus/audio_codec.h @@ -108,6 +108,15 @@ struct gbaudio_codec_info { int manager_id; 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 */ struct snd_soc_codec *codec; struct device *dev; @@ -139,7 +148,6 @@ struct gbaudio_codec_info { struct list_head widget_list; struct list_head codec_ctl_list; struct list_head widget_ctl_list; - struct mutex lock; }; struct gbaudio_dai *gbaudio_find_dai(struct gbaudio_codec_info *gbcodec, diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index 5fab393130a5..1651c14c87ba 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -140,6 +140,9 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 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; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -187,6 +190,9 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 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; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -281,6 +287,9 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = widget->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; info = (struct gb_audio_ctl_elem_info *)data->info; @@ -315,6 +324,9 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = widget->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; info = (struct gb_audio_ctl_elem_info *)data->info;