[alsa-devel] [PATCH] ALSA: ASoC: Fix memory leaks for the CS4270 driver
Takashi Iwai
tiwai at suse.de
Thu Sep 4 09:00:09 CEST 2008
At Wed, 3 Sep 2008 17:07:14 -0500,
Timur Tabi wrote:
>
> The CS4270 ASoC driver was not cleaning up resources completely during an
> I2C unbind. The same problem could occur if the bind failed.
>
> Rearranged a couple functions to eliminate an unnecessary function declaration.
>
> Signed-off-by: Timur Tabi <timur at freescale.com>
Thanks for the patch. But it can't be applied as is because of the
following reasons:
- In cs4270_remove(), cs4270_i2c_detach() is called after
snd_soc_free_pcms(). snd_soc_free_pcms() invokes snd_card_free()
inside, and this releases the all resources already, including the
control elements. That is, you free an already-freed item.
- The only case you'll need to care is the remaining controls at
error since the driver still runs in the stand-alone mode as
fallback. That is, the case snd_ctl_add() returns the error in
cs4270_probe().
[BTW, this case is usually a fatal error and should be handled so in
the caller side. But the error from attach_adapter callback is
ignored in i2c core, so this still remains.]
Well, looking the code again, you create only one element, and if
this fails, there is no other remaining element. Thus, there is
nothing to free there.
- The way you look for a kcontrol is wrong although it may work in
practice. A usual way is to remember the kctl and use it for
removal, or find the kctl via snd_ctl_find_id(), not _numid().
- Your patch merged in the ALSA tree already would conflict with this
patch. This is a mess, results in the whole rebase of git tree.
So, I think the code in 2.6.27 tree can stay as is. There is no leak
in the end. But, we can fix cs4270.c in the latest ALSA tree,
e.g. add a missing remove callback to release codec->reg_cache.
thanks,
Takashi
> ---
>
> This patch is a fix for 2.6.27. It applies only to the ASoC V1 version of the
> driver. A similar fix for the V2 version will be posted later.
>
> sound/soc/codecs/cs4270.c | 94 ++++++++++++++++++++++++++++++--------------
> 1 files changed, 64 insertions(+), 30 deletions(-)
>
> diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
> index 82d94f0..eb38e92 100644
> --- a/sound/soc/codecs/cs4270.c
> +++ b/sound/soc/codecs/cs4270.c
> @@ -490,29 +490,6 @@ static int cs4270_mute(struct snd_soc_dai *dai, int mute)
>
> #endif
>
> -static int cs4270_i2c_probe(struct i2c_client *, const struct i2c_device_id *);
> -
> -/* A list of non-DAPM controls that the CS4270 supports */
> -static const struct snd_kcontrol_new cs4270_snd_controls[] = {
> - SOC_DOUBLE_R("Master Playback Volume",
> - CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1)
> -};
> -
> -static const struct i2c_device_id cs4270_id[] = {
> - {"cs4270", 0},
> - {}
> -};
> -MODULE_DEVICE_TABLE(i2c, cs4270_id);
> -
> -static struct i2c_driver cs4270_i2c_driver = {
> - .driver = {
> - .name = "CS4270 I2C",
> - .owner = THIS_MODULE,
> - },
> - .id_table = cs4270_id,
> - .probe = cs4270_i2c_probe,
> -};
> -
> /*
> * Global variable to store socdev for i2c probe function.
> *
> @@ -530,6 +507,12 @@ static struct i2c_driver cs4270_i2c_driver = {
> */
> static struct snd_soc_device *cs4270_socdev;
>
> +/* A list of non-DAPM controls that the CS4270 supports */
> +static const struct snd_kcontrol_new cs4270_snd_controls[] = {
> + SOC_DOUBLE_R("Master Playback Volume",
> + CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1)
> +};
> +
> /*
> * Initialize the I2C interface of the CS4270
> *
> @@ -559,8 +542,7 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
> codec->reg_cache = kzalloc(CS4270_NUMREGS, GFP_KERNEL);
> if (!codec->reg_cache) {
> printk(KERN_ERR "cs4270: could not allocate register cache\n");
> - ret = -ENOMEM;
> - goto error;
> + return -ENOMEM;
> }
>
> /* Verify that we have a CS4270 */
> @@ -610,20 +592,72 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
> return 0;
>
> error:
> - if (codec->control_data) {
> - i2c_detach_client(i2c_client);
> - codec->control_data = NULL;
> + for (i = 1; i <= ARRAY_SIZE(cs4270_snd_controls); i++) {
> + struct snd_kcontrol *kctrl =
> + snd_ctl_find_numid(codec->card, i);
> +
> + if (kctrl)
> + snd_ctl_remove(codec->card, kctrl);
> }
>
> + codec->control_data = NULL;
> +
> kfree(codec->reg_cache);
> codec->reg_cache = NULL;
> codec->reg_cache_size = 0;
>
> - kfree(i2c_client);
> -
> return ret;
> }
>
> +/*
> + * Remove I2C interface of the CS4270
> + *
> + * Since this driver cannot be compiled as a module, the only way this
> + * function can be called is if the I2C subsystem unbinds it. This can be
> + * triggered, for instance, by writing the device ID to this drivers
> + * 'unbind' file in /sys.
> + */
> +static int cs4270_i2c_remove(struct i2c_client *i2c_client)
> +{
> + struct snd_soc_codec *codec = i2c_get_clientdata(i2c_client);
> +
> + if (codec) {
> + unsigned int i;
> +
> + for (i = 1; i <= ARRAY_SIZE(cs4270_snd_controls); i++) {
> + struct snd_kcontrol *kctrl =
> + snd_ctl_find_numid(codec->card, i);
> +
> + if (kctrl)
> + snd_ctl_remove(codec->card, kctrl);
> + }
> +
> + codec->control_data = NULL;
> +
> + kfree(codec->reg_cache);
> + codec->reg_cache = NULL;
> + codec->reg_cache_size = 0;
> + }
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id cs4270_id[] = {
> + {"cs4270", 0},
> + {}
> +};
> +MODULE_DEVICE_TABLE(i2c, cs4270_id);
> +
> +static struct i2c_driver cs4270_i2c_driver = {
> + .driver = {
> + .name = "cs4270",
> + .owner = THIS_MODULE,
> + },
> + .id_table = cs4270_id,
> + .probe = cs4270_i2c_probe,
> + .remove = cs4270_i2c_remove,
> +};
> +
> #endif /* USE_I2C*/
>
> struct snd_soc_dai cs4270_dai = {
> --
> 1.5.5
>
More information about the Alsa-devel
mailing list