From 1b4efdaf6d6053c8944cee0edba0969dc1be7d4b Mon Sep 17 00:00:00 2001
From: Dragos Tarcatu <dragos_tarcatu@mentor.com>
Date: Mon, 11 Nov 2019 16:20:38 -0600
Subject: [PATCH] ASoC: SOF: topology: free kcontrol memory on error
Git-commit: 1b4efdaf6d6053c8944cee0edba0969dc1be7d4b
Patch-mainline: v5.5-rc1
References: git-fixes
The volume and bytes kcontrols are currently not freeing their
memory on initialization failures. When an error occurs, all the
widgets loaded so far are unloaded via sof_widget_unload().
But this only happens for the widgets that got successfully loaded.
Fix that by kfree()-ing the allocated memory on load error.
Fixes: 311ce4fe7637d ("ASoC: SOF: Add support for loading topologies")
Reviewed-by: Paul Olaru <paul.olaru@nxp.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Dragos Tarcatu <dragos_tarcatu@mentor.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20191111222039.19651-1-pierre-louis.bossart@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Acked-by: Takashi Iwai <tiwai@suse.de>
---
sound/soc/sof/topology.c | 62 ++++++++++++++++++++++++++++++++++-------------
1 file changed, 45 insertions(+), 17 deletions(-)
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -437,18 +437,22 @@ static int sof_control_load_volume(struc
struct sof_ipc_ctrl_data *cdata;
int tlv[TLV_ITEMS];
unsigned int i;
- int ret;
+ int ret = 0;
/* validate topology data */
- if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN)
- return -EINVAL;
+ if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) {
+ ret = -EINVAL;
+ goto out;
+ }
/* init the volume get/put data */
scontrol->size = struct_size(scontrol->control_data, chanv,
le32_to_cpu(mc->num_channels));
scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL);
- if (!scontrol->control_data)
- return -ENOMEM;
+ if (!scontrol->control_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
scontrol->comp_id = sdev->next_comp_id;
scontrol->min_volume_step = le32_to_cpu(mc->min);
@@ -458,7 +462,7 @@ static int sof_control_load_volume(struc
/* set cmd for mixer control */
if (le32_to_cpu(mc->max) == 1) {
scontrol->cmd = SOF_CTRL_CMD_SWITCH;
- goto out;
+ goto skip;
}
scontrol->cmd = SOF_CTRL_CMD_VOLUME;
@@ -466,14 +470,15 @@ static int sof_control_load_volume(struc
/* extract tlv data */
if (get_tlv_data(kc->tlv.p, tlv) < 0) {
dev_err(sdev->dev, "error: invalid TLV data\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_free;
}
/* set up volume table */
ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1);
if (ret < 0) {
dev_err(sdev->dev, "error: setting up volume table\n");
- return ret;
+ goto out_free;
}
/* set default volume values to 0dB in control */
@@ -483,11 +488,16 @@ static int sof_control_load_volume(struc
cdata->chanv[i].value = VOL_ZERO_DB;
}
-out:
+skip:
dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n",
scontrol->comp_id, scontrol->num_channels);
- return 0;
+ return ret;
+
+out_free:
+ kfree(scontrol->control_data);
+out:
+ return ret;
}
static int sof_control_load_enum(struct snd_soc_component *scomp,
@@ -532,6 +542,7 @@ static int sof_control_load_bytes(struct
container_of(hdr, struct snd_soc_tplg_bytes_control, hdr);
struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value;
int max_size = sbe->max;
+ int ret = 0;
/* init the get/put bytes data */
scontrol->size = sizeof(struct sof_ipc_ctrl_data) +
@@ -540,13 +551,16 @@ static int sof_control_load_bytes(struct
if (scontrol->size > max_size) {
dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n",
scontrol->size, max_size);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
scontrol->control_data = kzalloc(max_size, GFP_KERNEL);
cdata = scontrol->control_data;
- if (!scontrol->control_data)
- return -ENOMEM;
+ if (!scontrol->control_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
scontrol->comp_id = sdev->next_comp_id;
scontrol->cmd = SOF_CTRL_CMD_BINARY;
@@ -561,23 +575,32 @@ static int sof_control_load_bytes(struct
if (cdata->data->magic != SOF_ABI_MAGIC) {
dev_err(sdev->dev, "error: Wrong ABI magic 0x%08x.\n",
cdata->data->magic);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_free;
}
if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION,
cdata->data->abi)) {
dev_err(sdev->dev,
"error: Incompatible ABI version 0x%08x.\n",
cdata->data->abi);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_free;
}
if (cdata->data->size + sizeof(const struct sof_abi_hdr) !=
le32_to_cpu(control->priv.size)) {
dev_err(sdev->dev,
"error: Conflict in bytes vs. priv size.\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_free;
}
}
- return 0;
+
+ return ret;
+
+out_free:
+ kfree(scontrol->control_data);
+out:
+ return ret;
}
/*
@@ -1082,6 +1105,11 @@ static int sof_control_load(struct snd_s
return 0;
}
+ if (ret < 0) {
+ kfree(scontrol);
+ return ret;
+ }
+
dobj->private = scontrol;
list_add(&scontrol->list, &sdev->kcontrol_list);
return ret;