Commit 76c49909 authored by Mark Brown's avatar Mark Brown

Merge series "ASoC: topology: fix error handling flow" from Pierre-Louis...

Merge series "ASoC: topology: fix error handling flow" from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>:

While experimenting and introducing errors in Baytrail topology files
until I got them right, I encountered multiple kernel oopses and
memory leaks. This is a first batch to harden the code, but we should
probably think of a tool to fuzz the topology...

Pierre-Louis Bossart (5):
  ASoC: topology: fix kernel oops on route addition error
  ASoC: topology: fix tlvs in error handling for widget_dmixer
  ASoC: topology: use break on errors, not continue
  ASoC: topology: factor kfree(se) in error handling
  ASoC: topology: add more logs when topology load fails.

 sound/soc/soc-topology.c | 97 ++++++++++++++++++++++++----------------
 1 file changed, 58 insertions(+), 39 deletions(-)

base-commit: a5911ac5
--
2.25.1
parents 81e7d4ce 8bf9475f
......@@ -6956,6 +6956,7 @@ M: Timur Tabi <timur@kernel.org>
M: Nicolin Chen <nicoleotsuka@gmail.com>
M: Xiubo Li <Xiubo.Lee@gmail.com>
R: Fabio Estevam <festevam@gmail.com>
R: Shengjiu Wang <shengjiu.wang@gmail.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
L: linuxppc-dev@lists.ozlabs.org
S: Maintained
......@@ -11333,17 +11334,17 @@ F: drivers/iio/adc/at91-sama5d2_adc.c
F: include/dt-bindings/iio/adc/at91-sama5d2_adc.h
MICROCHIP SAMA5D2-COMPATIBLE SHUTDOWN CONTROLLER
M: Nicolas Ferre <nicolas.ferre@microchip.com>
M: Claudiu Beznea <claudiu.beznea@microchip.com>
S: Supported
F: drivers/power/reset/at91-sama5d2_shdwc.c
MICROCHIP SPI DRIVER
M: Nicolas Ferre <nicolas.ferre@microchip.com>
M: Tudor Ambarus <tudor.ambarus@microchip.com>
S: Supported
F: drivers/spi/spi-atmel.*
MICROCHIP SSC DRIVER
M: Nicolas Ferre <nicolas.ferre@microchip.com>
M: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Supported
F: drivers/misc/atmel-ssc.c
......
......@@ -426,6 +426,8 @@ int devm_snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *component_driver,
struct snd_soc_dai_driver *dai_drv, int num_dai);
void snd_soc_unregister_component(struct device *dev);
void snd_soc_unregister_component_by_driver(struct device *dev,
const struct snd_soc_component_driver *component_driver);
struct snd_soc_component *snd_soc_lookup_component_nolocked(struct device *dev,
const char *driver_name);
struct snd_soc_component *snd_soc_lookup_component(struct device *dev,
......
......@@ -232,9 +232,7 @@ static int snd_acp3x_probe(struct pci_dev *pci,
}
pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
pm_runtime_use_autosuspend(&pci->dev);
pm_runtime_set_active(&pci->dev);
pm_runtime_put_noidle(&pci->dev);
pm_runtime_enable(&pci->dev);
pm_runtime_allow(&pci->dev);
return 0;
......@@ -303,7 +301,7 @@ static void snd_acp3x_remove(struct pci_dev *pci)
ret = acp3x_deinit(adata->acp3x_base);
if (ret)
dev_err(&pci->dev, "ACP de-init failed\n");
pm_runtime_disable(&pci->dev);
pm_runtime_forbid(&pci->dev);
pm_runtime_get_noresume(&pci->dev);
pci_disable_msi(pci);
pci_release_regions(pci);
......
......@@ -992,16 +992,17 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
rt5682->hs_jack = hs_jack;
if (!rt5682->is_sdw) {
if (!hs_jack) {
regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
RT5682_POW_JDH | RT5682_POW_JDL, 0);
cancel_delayed_work_sync(&rt5682->jack_detect_work);
return 0;
}
if (!hs_jack) {
regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
RT5682_POW_JDH | RT5682_POW_JDL, 0);
cancel_delayed_work_sync(&rt5682->jack_detect_work);
return 0;
}
if (!rt5682->is_sdw) {
switch (rt5682->pdata.jd_src) {
case RT5682_JD1:
snd_soc_component_update_bits(component,
......
......@@ -219,19 +219,32 @@ static int rockchip_sound_dmic_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static int rockchip_sound_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
return snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
8000, 96000);
}
static const struct snd_soc_ops rockchip_sound_max98357a_ops = {
.startup = rockchip_sound_startup,
.hw_params = rockchip_sound_max98357a_hw_params,
};
static const struct snd_soc_ops rockchip_sound_rt5514_ops = {
.startup = rockchip_sound_startup,
.hw_params = rockchip_sound_rt5514_hw_params,
};
static const struct snd_soc_ops rockchip_sound_da7219_ops = {
.startup = rockchip_sound_startup,
.hw_params = rockchip_sound_da7219_hw_params,
};
static const struct snd_soc_ops rockchip_sound_dmic_ops = {
.startup = rockchip_sound_startup,
.hw_params = rockchip_sound_dmic_hw_params,
};
......
......@@ -2513,6 +2513,33 @@ int snd_soc_register_component(struct device *dev,
}
EXPORT_SYMBOL_GPL(snd_soc_register_component);
/**
* snd_soc_unregister_component_by_driver - Unregister component using a given driver
* from the ASoC core
*
* @dev: The device to unregister
* @component_driver: The component driver to unregister
*/
void snd_soc_unregister_component_by_driver(struct device *dev,
const struct snd_soc_component_driver *component_driver)
{
struct snd_soc_component *component;
if (!component_driver)
return;
mutex_lock(&client_mutex);
component = snd_soc_lookup_component_nolocked(dev, component_driver->name);
if (!component)
goto out;
snd_soc_del_component_unlocked(component);
out:
mutex_unlock(&client_mutex);
}
EXPORT_SYMBOL_GPL(snd_soc_unregister_component_by_driver);
/**
* snd_soc_unregister_component - Unregister all related component
* from the ASoC core
......
......@@ -48,7 +48,9 @@ EXPORT_SYMBOL_GPL(devm_snd_soc_register_dai);
static void devm_component_release(struct device *dev, void *res)
{
snd_soc_unregister_component(*(struct device **)res);
const struct snd_soc_component_driver **cmpnt_drv = res;
snd_soc_unregister_component_by_driver(dev, *cmpnt_drv);
}
/**
......@@ -65,7 +67,7 @@ int devm_snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *cmpnt_drv,
struct snd_soc_dai_driver *dai_drv, int num_dai)
{
struct device **ptr;
const struct snd_soc_component_driver **ptr;
int ret;
ptr = devres_alloc(devm_component_release, sizeof(*ptr), GFP_KERNEL);
......@@ -74,7 +76,7 @@ int devm_snd_soc_register_component(struct device *dev,
ret = snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai);
if (ret == 0) {
*ptr = dev;
*ptr = cmpnt_drv;
devres_add(dev, ptr);
} else {
devres_free(ptr);
......
......@@ -478,7 +478,7 @@ void snd_dmaengine_pcm_unregister(struct device *dev)
pcm = soc_component_to_pcm(component);
snd_soc_unregister_component(dev);
snd_soc_unregister_component_by_driver(dev, component->driver);
dmaengine_pcm_release_chan(pcm);
kfree(pcm);
}
......
......@@ -741,7 +741,8 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count,
struct snd_soc_tplg_bytes_control *be;
struct soc_bytes_ext *sbe;
struct snd_kcontrol_new kc;
int i, err;
int i;
int err = 0;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_bytes_control), count,
......@@ -786,7 +787,7 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count,
if (err) {
soc_control_err(tplg, &be->hdr, be->hdr.name);
kfree(sbe);
continue;
break;
}
/* pass control to driver for optional further init */
......@@ -796,7 +797,7 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count,
dev_err(tplg->dev, "ASoC: failed to init %s\n",
be->hdr.name);
kfree(sbe);
continue;
break;
}
/* register control here */
......@@ -806,12 +807,12 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count,
dev_err(tplg->dev, "ASoC: failed to add %s\n",
be->hdr.name);
kfree(sbe);
continue;
break;
}
list_add(&sbe->dobj.list, &tplg->comp->dobj_list);
}
return 0;
return err;
}
......@@ -821,7 +822,8 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
struct snd_soc_tplg_mixer_control *mc;
struct soc_mixer_control *sm;
struct snd_kcontrol_new kc;
int i, err;
int i;
int err = 0;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_mixer_control),
......@@ -880,7 +882,7 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
if (err) {
soc_control_err(tplg, &mc->hdr, mc->hdr.name);
kfree(sm);
continue;
break;
}
/* create any TLV data */
......@@ -889,7 +891,7 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
mc->hdr.name);
kfree(sm);
continue;
break;
}
/* pass control to driver for optional further init */
......@@ -900,7 +902,7 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
mc->hdr.name);
soc_tplg_free_tlv(tplg, &kc);
kfree(sm);
continue;
break;
}
/* register control here */
......@@ -911,13 +913,13 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
mc->hdr.name);
soc_tplg_free_tlv(tplg, &kc);
kfree(sm);
continue;
break;
}
list_add(&sm->dobj.list, &tplg->comp->dobj_list);
}
return 0;
return err;
}
static int soc_tplg_denum_create_texts(struct soc_enum *se,
......@@ -997,7 +999,8 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
struct snd_soc_tplg_enum_control *ec;
struct soc_enum *se;
struct snd_kcontrol_new kc;
int i, ret, err;
int i;
int err = 0;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_enum_control),
......@@ -1052,8 +1055,7 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
dev_err(tplg->dev,
"ASoC: could not create values for %s\n",
ec->hdr.name);
kfree(se);
continue;
goto err_denum;
}
/* fall through */
case SND_SOC_TPLG_CTL_ENUM:
......@@ -1064,24 +1066,22 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
dev_err(tplg->dev,
"ASoC: could not create texts for %s\n",
ec->hdr.name);
kfree(se);
continue;
goto err_denum;
}
break;
default:
err = -EINVAL;
dev_err(tplg->dev,
"ASoC: invalid enum control type %d for %s\n",
ec->hdr.ops.info, ec->hdr.name);
kfree(se);
continue;
goto err_denum;
}
/* map io handlers */
err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg);
if (err) {
soc_control_err(tplg, &ec->hdr, ec->hdr.name);
kfree(se);
continue;
goto err_denum;
}
/* pass control to driver for optional further init */
......@@ -1090,24 +1090,25 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
ec->hdr.name);
kfree(se);
continue;
goto err_denum;
}
/* register control here */
ret = soc_tplg_add_kcontrol(tplg,
&kc, &se->dobj.control.kcontrol);
if (ret < 0) {
err = soc_tplg_add_kcontrol(tplg,
&kc, &se->dobj.control.kcontrol);
if (err < 0) {
dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n",
ec->hdr.name);
kfree(se);
continue;
goto err_denum;
}
list_add(&se->dobj.list, &tplg->comp->dobj_list);
}
return 0;
err_denum:
kfree(se);
return err;
}
static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
......@@ -1261,17 +1262,30 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
list_add(&routes[i]->dobj.list, &tplg->comp->dobj_list);
ret = soc_tplg_add_route(tplg, routes[i]);
if (ret < 0)
if (ret < 0) {
dev_err(tplg->dev, "ASoC: topology: add_route failed: %d\n", ret);
/*
* this route was added to the list, it will
* be freed in remove_route() so increment the
* counter to skip it in the error handling
* below.
*/
i++;
break;
}
/* add route, but keep going if some fail */
snd_soc_dapm_add_routes(dapm, routes[i], 1);
}
/* free memory allocated for all dapm routes in case of error */
if (ret < 0)
for (i = 0; i < count ; i++)
kfree(routes[i]);
/*
* free memory allocated for all dapm routes not added to the
* list in case of error
*/
if (ret < 0) {
while (i < count)
kfree(routes[i++]);
}
/*
* free pointer to array of dapm routes as this is no longer needed.
......@@ -1349,8 +1363,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
mc->hdr.name);
kfree(sm);
continue;
goto err_sm;
}
/* pass control to driver for optional further init */
......@@ -1359,7 +1372,6 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
mc->hdr.name);
soc_tplg_free_tlv(tplg, &kc[i]);
goto err_sm;
}
}
......@@ -1367,6 +1379,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
err_sm:
for (; i >= 0; i--) {
soc_tplg_free_tlv(tplg, &kc[i]);
sm = (struct soc_mixer_control *)kc[i].private_value;
kfree(sm);
kfree(kc[i].name);
......@@ -2731,15 +2744,21 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
/* make sure header is valid before loading */
ret = soc_valid_header(tplg, hdr);
if (ret < 0)
if (ret < 0) {
dev_err(tplg->dev,
"ASoC: topology: invalid header: %d\n", ret);
return ret;
else if (ret == 0)
} else if (ret == 0) {
break;
}
/* load the header object */
ret = soc_tplg_load_header(tplg, hdr);
if (ret < 0)
if (ret < 0) {
dev_err(tplg->dev,
"ASoC: topology: could not load header: %d\n", ret);
return ret;
}
/* goto next header */
tplg->hdr_pos += le32_to_cpu(hdr->payload_size) +
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment