Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd96e41d authored by Ajit Pandey's avatar Ajit Pandey Committed by ssizon
Browse files

asoc: msm-pcm: Added lock in controls _put() and _get() callback



There is race condition around private data used in put() and get()
of few mixer ctls with close() callback. Added global mutex lock and
code changes to protect such critical section by accessing such lock.

Change-Id: Ic160b0c7c29d0ea5d517b3bacc53828d8d61e085
Signed-off-by: default avatarAjit Pandey <ajitp@codeaurora.org>
parent 17d96c69
Loading
Loading
Loading
Loading
+74 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/time.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -871,6 +872,14 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream)

	pr_debug("%s: cmd_pending 0x%lx\n", __func__, prtd->cmd_pending);

	pdata = (struct msm_plat_data *)
		dev_get_drvdata(soc_prtd->platform->dev);
	if (!pdata) {
		pr_err("%s: platform data is NULL\n", __func__);
		return -EINVAL;
	}

	mutex_lock(&pdata->lock);
	if (prtd->audio_client) {
		dir = IN;

@@ -913,6 +922,7 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
	msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd);
	kfree(prtd);
	runtime->private_data = NULL;
	mutex_unlock(&pdata->lock);

	return 0;
}
@@ -1007,8 +1017,18 @@ static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
	struct msm_audio *prtd = runtime->private_data;
	int dir = OUT;
	struct msm_plat_data *pdata;

	pr_debug("%s\n", __func__);

	pdata = (struct msm_plat_data *)
		dev_get_drvdata(soc_prtd->platform->dev);
	if (!pdata) {
		pr_err("%s: platform data is NULL\n", __func__);
		return -EINVAL;
	}

	mutex_lock(&pdata->lock);
	if (prtd->audio_client) {
		q6asm_cmd(prtd->audio_client, CMD_CLOSE);
		q6asm_audio_client_buf_free_contiguous(dir,
@@ -1020,6 +1040,7 @@ static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
		SNDRV_PCM_STREAM_CAPTURE);
	kfree(prtd);
	runtime->private_data = NULL;
	mutex_unlock(&pdata->lock);

	return 0;
}
@@ -1188,6 +1209,7 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
		goto done;
	}

	mutex_lock(&pdata->lock);
	event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data;
	if ((event_data->event_type < ADSP_STREAM_PP_EVENT) ||
	    (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) {
@@ -1219,6 +1241,7 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
		pr_err("%s: failed to send stream event cmd, err = %d\n",
			__func__, ret);
done:
	mutex_unlock(&pdata->lock);
	return ret;
}

@@ -1398,21 +1421,35 @@ static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
		vol->pcm->streams[vol->stream].substream;
	struct msm_audio *prtd;
	int volume = ucontrol->value.integer.value[0];
	struct msm_plat_data *pdata = NULL;
	struct snd_soc_pcm_runtime *soc_prtd = NULL;

	pr_debug("%s: volume : 0x%x\n", __func__, volume);
	if (!substream) {
		pr_err("%s substream not found\n", __func__);
		pr_err("%s: substream not found\n", __func__);
		return -ENODEV;
	}
	if (!substream->runtime) {
		pr_err("%s substream runtime not found\n", __func__);
	soc_prtd = substream->private_data;
	if (!substream->runtime || !soc_prtd) {
		pr_err("%s: substream runtime or private_data not found\n",
				__func__);
		return 0;
	}

	pdata = (struct msm_plat_data *)
		dev_get_drvdata(soc_prtd->platform->dev);
	if (!pdata) {
		pr_err("%s: pdata not found\n", __func__);
		return -ENODEV;
	}

	mutex_lock(&pdata->lock);
	prtd = substream->runtime->private_data;
	if (prtd) {
		rc = msm_pcm_set_volume(prtd, volume);
		prtd->volume = volume;
	}
	mutex_unlock(&pdata->lock);
	return rc;
}

@@ -1581,14 +1618,28 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
	struct snd_pcm_substream *substream;
	struct msm_audio *prtd;
	struct snd_soc_pcm_runtime *rtd = NULL;
	struct msm_plat_data *pdata = NULL;

	pr_debug("%s", __func__);
	substream = snd_pcm_chmap_substream(info, idx);
	if (!substream)
		return -ENODEV;
	if (!substream->runtime)

	rtd = substream->private_data;
	if (rtd) {
		pdata = (struct msm_plat_data *)
				dev_get_drvdata(rtd->platform->dev);
		if (!pdata) {
			pr_err("%s: pdata not found\n", __func__);
			return -ENODEV;
		}
	}

	if (!substream->runtime || !rtd)
		return 0;

	mutex_lock(&pdata->lock);
	prtd = substream->runtime->private_data;
	if (prtd) {
		prtd->set_channel_map = true;
@@ -1596,6 +1647,7 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
				prtd->channel_map[i] =
				(char)(ucontrol->value.integer.value[i]);
	}
	mutex_unlock(&pdata->lock);
	return 0;
}

@@ -1607,16 +1659,30 @@ static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
	struct snd_pcm_substream *substream;
	struct msm_audio *prtd;
	struct snd_soc_pcm_runtime *rtd = NULL;
	struct msm_plat_data *pdata = NULL;

	pr_debug("%s", __func__);
	substream = snd_pcm_chmap_substream(info, idx);
	if (!substream)
		return -ENODEV;

	rtd = substream->private_data;
	if (rtd) {
		pdata = (struct msm_plat_data *)
				dev_get_drvdata(rtd->platform->dev);
		if (!pdata) {
			pr_err("%s: pdata not found\n", __func__);
			return -ENODEV;
		}
	}

	memset(ucontrol->value.integer.value, 0,
		sizeof(ucontrol->value.integer.value));
	if (!substream->runtime)
	if (!substream->runtime || !rtd)
		return 0; /* no channels set */

	mutex_lock(&pdata->lock);
	prtd = substream->runtime->private_data;

	if (prtd && prtd->set_channel_map == true) {
@@ -1628,6 +1694,7 @@ static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
			ucontrol->value.integer.value[i] = 0;
	}

	mutex_unlock(&pdata->lock);
	return 0;
}

@@ -2089,6 +2156,7 @@ static int msm_pcm_probe(struct platform_device *pdev)
		pdata->perf_mode = LEGACY_PCM_MODE;
	}

	mutex_init(&pdata->lock);
	dev_set_drvdata(&pdev->dev, pdata);


@@ -2103,6 +2171,7 @@ static int msm_pcm_remove(struct platform_device *pdev)
	struct msm_plat_data *pdata;

	pdata = dev_get_drvdata(&pdev->dev);
	mutex_destroy(&pdata->lock);
	kfree(pdata);
	snd_soc_unregister_platform(&pdev->dev);
	return 0;
+2 −1
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 Google, Inc.
 * Copyright (C) 2008 HTC Corporation
 * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2017, 2019 The Linux Foundation. All rights reserved.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
@@ -125,6 +125,7 @@ struct output_meta_data_st {
struct msm_plat_data {
	int perf_mode;
	struct snd_pcm *pcm;
	struct mutex lock;
};

#endif /*_MSM_PCM_H*/