Blob Blame History Raw
From c3ec8ac82105e9589dcd72636b6fd114db690d55 Mon Sep 17 00:00:00 2001
From: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Date: Fri, 17 Jul 2020 13:19:49 +0300
Subject: [PATCH] ASoC: hdac_hda: fix memleak on module unload
Git-commit: c3ec8ac82105e9589dcd72636b6fd114db690d55
Patch-mainline: v5.9-rc1
References: jsc#SLE-16518

The hdac_hda remove implementation fails to free the hda codec
resources, leading to memleaks at module unload. This gap has been there
from the start, commit 6bae5ea94989 ("ASoC: hdac_hda: add asoc
extension for legacy HDA codec drivers").

Instead of duplicating the cleanup logic, use the common
snd_hda_codec_cleanup_for_unbind() to free the resources. Remove
existing code in hdac_hda to cleanup "codec.jackpoll_work" and call to
snd_hdac_regmap_exit(), as these are already done in
snd_hda_codec_cleanup_for_unbind().

The cleanup is done in ASoC component remove() callback and not in the
HDAC bus hdev_detach(). This is done to ensure the codec specific
cleanup routines are run before the parent card is freed.

Fixes: 6bae5ea94989 ("ASoC: hdac_hda: add asoc extension for legacy HDA codec drivers")
Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Buglink: https://github.com/thesofproject/linux/issues/2195
Link: https://lore.kernel.org/r/20200717101950.3885187-2-kai.vehmanen@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Acked-by: Takashi Iwai <tiwai@suse.de>

---
 sound/soc/codecs/hdac_hda.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index 72bd779bf942..74574b9180a5 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -513,6 +513,7 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
 	struct hdac_hda_priv *hda_pvt =
 		      snd_soc_component_get_drvdata(component);
 	struct hdac_device *hdev = &hda_pvt->codec.core;
+	struct hda_codec *codec = &hda_pvt->codec;
 	struct hdac_ext_link *hlink = NULL;
 
 	hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev));
@@ -524,7 +525,10 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
 	pm_runtime_disable(&hdev->dev);
 	snd_hdac_ext_bus_link_put(hdev->bus, hlink);
 
-	snd_hdac_regmap_exit(hdev);
+	if (codec->patch_ops.free)
+		codec->patch_ops.free(codec);
+
+	snd_hda_codec_cleanup_for_unbind(codec);
 }
 
 static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {
@@ -608,12 +612,10 @@ static int hdac_hda_dev_probe(struct hdac_device *hdev)
 
 static int hdac_hda_dev_remove(struct hdac_device *hdev)
 {
-	struct hdac_hda_priv *hda_pvt;
-
-	hda_pvt = dev_get_drvdata(&hdev->dev);
-	if (hda_pvt && hda_pvt->codec.registered)
-		cancel_delayed_work_sync(&hda_pvt->codec.jackpoll_work);
-
+	/*
+	 * Resources are freed in hdac_hda_codec_remove(). This
+	 * function is kept to keep hda_codec_driver_remove() happy.
+	 */
 	return 0;
 }
 
-- 
2.16.4