From 43442ea87acb55d60545a4849d1b41e4aee3c5b0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 23 Oct 2026 09:47:53 +0300 Subject: [PATCH 1/2] ASoC: SOF: topology: Remove loaded topologies on load failure snd_sof_load_topology() can fail after one or more topology files have been loaded: requesting a subsequent topology file, allocating the path of a feature topology, sof_complete() or snd_ctl_led_request() can fail at that point. snd_soc_tplg_component_load() only removes the topology objects when the load itself fails. When the component probe fails the ASoC core does not call the component remove callback, so the loaded topology objects are left behind referencing freed DAPM data. Call snd_soc_tplg_component_remove() whenever snd_sof_load_topology() fails. If the error came from snd_soc_tplg_component_load() the objects have been removed already and the second call is harmless. Drop the comments which claimed that the topology is removed on request_firmware() failure. Fixes: 6d5997c412cc ("ASoC: SOF: topology: load multiple topologies") Signed-off-by: Peter Ujfalusi --- sound/soc/sof/topology.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 750916b57def74..d485035e9180f5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2575,12 +2575,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, tplg_files[i], scomp->dev); if (ret < 0) { - /* - * snd_soc_tplg_component_remove(scomp) will be called - * if snd_soc_tplg_component_load(scomp) failed and all - * objects in the scomp will be removed. No need to call - * snd_soc_tplg_component_remove(scomp) here. - */ dev_err(scomp->dev, "tplg request firmware %s failed err: %d\n", tplg_files[i], ret); goto out; @@ -2613,12 +2607,6 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, feature_topology, scomp->dev); if (ret < 0) { - /* - * snd_soc_tplg_component_remove(scomp) will be called - * if snd_soc_tplg_component_load(scomp) failed and all - * objects in the scomp will be removed. No need to call - * snd_soc_tplg_component_remove(scomp) here. - */ dev_warn(scomp->dev, "feature tplg request firmware %s failed err: %d\n", feature_topologies[i], ret); /* @@ -2653,6 +2641,14 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) if (ret >= 0 && sdev->led_present) ret = snd_ctl_led_request(); + /* + * Remove the topology on any error as .remove is not called when the + * component probe fails. If snd_soc_tplg_component_load() failed, it + * has removed it already and this call is a no-op. + */ + if (ret < 0) + snd_soc_tplg_component_remove(scomp); + return ret; } EXPORT_SYMBOL(snd_sof_load_topology); From 210ddc35fb3da080f00bb381effa48905f2cc647 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 22 Oct 2026 14:23:55 +0300 Subject: [PATCH 2/2] ASoC: SOF: debug: List the loaded topology files in fw_profile/tplg_name The fw_profile/tplg_name debugfs file exposes the topology file name selected for the platform. When function topologies are used, the selected name is a placeholder (sof--dummy.tplg) which does not tell which topology files have actually been loaded. Replace the static name with a list of the loaded topology files, one per line, set by snd_sof_load_topology() after a successful load and cleared again when the loading fails or when the topology is removed in sof_pcm_remove(). The topology path prefix is dropped from the names as it is exposed separately via fw_profile/tplg_path. The file is backed by a seq_file reader instead of debugfs_create_str() so that the list can be replaced under a mutex without racing with concurrent readers. While no topology is loaded the selected topology name is shown. For monolithic topologies the content of the file is unchanged. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 1 + sound/soc/sof/debug.c | 27 ++++++++++++++++++++-- sound/soc/sof/pcm.c | 3 +++ sound/soc/sof/sof-priv.h | 4 ++++ sound/soc/sof/topology.c | 49 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 1f38fe03e49395..b275c091245785 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -699,6 +699,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) mutex_init(&sdev->ipc_client_mutex); mutex_init(&sdev->client_event_handler_mutex); mutex_init(&sdev->dsp_fw_boot_mutex); + mutex_init(&sdev->loaded_tplg_names_mutex); /* set default timeouts if none provided */ if (plat_data->desc->ipc_timeout == 0) diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 9801c875dd7018..1ac13ddc5d1306 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include "sof-priv.h" @@ -313,6 +314,28 @@ int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); +static int sof_dbg_tplg_name_show(struct seq_file *m, void *v) +{ + struct snd_sof_dev *sdev = m->private; + + /* fall back to the selected topology name while no topology is loaded */ + mutex_lock(&sdev->loaded_tplg_names_mutex); + seq_printf(m, "%s\n", sdev->loaded_tplg_names ?: sdev->pdata->tplg_filename); + mutex_unlock(&sdev->loaded_tplg_names_mutex); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(sof_dbg_tplg_name); + +/* Takes ownership of the devm allocated tplg_names, NULL clears the list */ +void sof_debug_set_tplg_name(struct snd_sof_dev *sdev, char *tplg_names) +{ + mutex_lock(&sdev->loaded_tplg_names_mutex); + devm_kfree(sdev->dev, sdev->loaded_tplg_names); + sdev->loaded_tplg_names = tplg_names; + mutex_unlock(&sdev->loaded_tplg_names_mutex); +} + int snd_sof_dbg_init(struct snd_sof_dev *sdev) { const struct snd_sof_dsp_ops *ops = sof_ops(sdev); @@ -355,8 +378,8 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) (char **)&plat_data->tplg_filename_prefix); debugfs_create_str("fw_name", 0444, fw_profile, (char **)&plat_data->fw_filename); - debugfs_create_str("tplg_name", 0444, fw_profile, - (char **)&plat_data->tplg_filename); + debugfs_create_file("tplg_name", 0444, fw_profile, sdev, + &sof_dbg_tplg_name_fops); debugfs_create_u32("ipc_type", 0444, fw_profile, (u32 *)&plat_data->ipc_type); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 0161c0ae8bbdb4..47e0d312946835 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -808,8 +808,11 @@ static int sof_pcm_probe(struct snd_soc_component *component) static void sof_pcm_remove(struct snd_soc_component *component) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + /* remove topology */ snd_soc_tplg_component_remove(component); + sof_debug_set_tplg_name(sdev, NULL); } static int sof_pcm_ack(struct snd_soc_component *component, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 83e4d83cc27cbb..fb5d25454233ef 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -656,6 +656,9 @@ struct snd_sof_dev { struct snd_soc_component *component; u32 enabled_cores_mask; /* keep track of enabled cores */ bool led_present; + /* newline separated list of the loaded topology files for debugfs */ + char *loaded_tplg_names; + struct mutex loaded_tplg_names_mutex; /* protects loaded_tplg_names */ /* FW configuration */ struct sof_ipc_window *info_window; @@ -798,6 +801,7 @@ static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_i */ int snd_sof_dbg_init(struct snd_sof_dev *sdev); void snd_sof_free_debug(struct snd_sof_dev *sdev); +void sof_debug_set_tplg_name(struct snd_sof_dev *sdev, char *tplg_names); int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, void *base, size_t size, const char *name, mode_t mode); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index d485035e9180f5..3de24acc45e7f9 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2509,11 +2509,40 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { .bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops), }; +/* + * Append the name of a loaded topology file to the list exposed via debugfs. + * The topology path prefix is dropped as it is exposed separately. + */ +static int sof_tplg_names_append(struct snd_sof_dev *sdev, char **names, + const char *file) +{ + const char *prefix = sdev->pdata->tplg_filename_prefix; + size_t prefix_len = strlen(prefix); + char *new_names; + + if (!strncmp(file, prefix, prefix_len) && file[prefix_len] == '/') + file += prefix_len + 1; + + if (*names) + new_names = devm_kasprintf(sdev->dev, GFP_KERNEL, "%s\n%s", + *names, file); + else + new_names = devm_kstrdup(sdev->dev, file, GFP_KERNEL); + if (!new_names) + return -ENOMEM; + + devm_kfree(sdev->dev, *names); + *names = new_names; + + return 0; +} + int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_pdata *sof_pdata = sdev->pdata; const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; + char *tplg_names = NULL; int tplg_cnt = 0; int ret; int i; @@ -2590,6 +2619,10 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) tplg_files[i], ret); goto out; } + + ret = sof_tplg_names_append(sdev, &tplg_names, tplg_files[i]); + if (ret < 0) + goto out; } /* Loading user defined topologies */ @@ -2632,12 +2665,24 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) */ goto out; } + + ret = sof_tplg_names_append(sdev, &tplg_names, feature_topology); + if (ret < 0) + goto out; } /* call sof_complete when topologies are loaded successfully */ ret = sof_complete(scomp); + if (ret < 0) + goto out; + + /* Update the list of loaded topologies exposed via debugfs */ + sof_debug_set_tplg_name(sdev, tplg_names); + tplg_names = NULL; out: + devm_kfree(sdev->dev, tplg_names); + if (ret >= 0 && sdev->led_present) ret = snd_ctl_led_request(); @@ -2646,8 +2691,10 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) * component probe fails. If snd_soc_tplg_component_load() failed, it * has removed it already and this call is a no-op. */ - if (ret < 0) + if (ret < 0) { snd_soc_tplg_component_remove(scomp); + sof_debug_set_tplg_name(sdev, NULL); + } return ret; }