greybus: audio: use variable 'is_connected' to maintain module state
authorVaibhav Agarwal <vaibhav.agarwal@linaro.org>
Thu, 28 Jan 2016 15:45:40 +0000 (21:15 +0530)
committerGreg Kroah-Hartman <gregkh@google.com>
Wed, 3 Feb 2016 05:46:54 +0000 (21:46 -0800)
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 <vaibhav.agarwal@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/audio_codec.c
drivers/staging/greybus/audio_codec.h
drivers/staging/greybus/audio_topology.c

index ad28c10fa154c6378bdbae20ac2875f015385d09..c05ab4fb875430393237d9ed21eec89fe795d836 100644 (file)
@@ -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);
index 56110913b70e7def241b26be7609266fd20a738c..4c19bd8844881fadafe513ad17c623f4cbdcdefa 100644 (file)
@@ -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,
index 5fab393130a596c3675005c2cb0698c6b2597f1c..1651c14c87ba95aab70b6a9b4da61abc207c9027 100644 (file)
@@ -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;