summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorVaibhav Agarwal2016-01-28 16:45:40 +0100
committerGreg Kroah-Hartman2016-02-03 06:46:54 +0100
commit3994e0b139c709047cdeb44b6c28cfb39f89f3f2 (patch)
tree6cd7caf1fd59c104d0c0431adb80ef657d0eb973 /drivers
parentgreybus: audio: codec driver cleanup (diff)
downloadkernel-qcow2-linux-3994e0b139c709047cdeb44b6c28cfb39f89f3f2.tar.gz
kernel-qcow2-linux-3994e0b139c709047cdeb44b6c28cfb39f89f3f2.tar.xz
kernel-qcow2-linux-3994e0b139c709047cdeb44b6c28cfb39f89f3f2.zip
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 <vaibhav.agarwal@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/greybus/audio_codec.c17
-rw-r--r--drivers/staging/greybus/audio_codec.h10
-rw-r--r--drivers/staging/greybus/audio_topology.c12
3 files changed, 38 insertions, 1 deletions
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;