| From cc1cbcd3bf7598c9b31be7074e2c1f114b290e90 Mon Sep 17 00:00:00 2001 |
| From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> |
| Date: Fri, 24 Jan 2020 15:36:21 -0600 |
| Subject: [PATCH] ASoC: SOF: core: release resources on errors in |
| probe_continue |
| |
| commit 410e5e55c9c1c9c0d452ac5b9adb37b933a7747e upstream. |
| |
| The initial intent of releasing resources in the .remove does not work |
| well with HDaudio codecs. If the probe_continue() fails in a work |
| queue, e.g. due to missing firmware or authentication issues, we don't |
| release any resources, and as a result the kernel oopses during |
| suspend operations. |
| |
| The suggested fix is to release all resources during errors in |
| probe_continue(), and use fw_state to track resource allocation |
| state, so that .remove does not attempt to release the same |
| hardware resources twice. PM operations are also modified so that |
| no action is done if DSP resources have been freed due to |
| an error at probe. |
| |
| Reported-by: Takashi Iwai <tiwai@suse.de> |
| Co-developed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> |
| Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> |
| Bugzilla: http://bugzilla.suse.com/show_bug.cgi?id=1161246 |
| Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> |
| Reviewed-by: Takashi Iwai <tiwai@suse.de> |
| Link: https://lore.kernel.org/r/20200124213625.30186-4-pierre-louis.bossart@linux.intel.com |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c |
| index 7825a86fad27..3c1aa4f12427 100644 |
| --- a/sound/soc/sof/core.c |
| +++ b/sound/soc/sof/core.c |
| @@ -440,7 +440,6 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) |
| |
| return 0; |
| |
| -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE) |
| fw_trace_err: |
| snd_sof_free_trace(sdev); |
| fw_run_err: |
| @@ -451,22 +450,10 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) |
| snd_sof_free_debug(sdev); |
| dbg_err: |
| snd_sof_remove(sdev); |
| -#else |
| - |
| - /* |
| - * when the probe_continue is handled in a work queue, the |
| - * probe does not fail so we don't release resources here. |
| - * They will be released with an explicit call to |
| - * snd_sof_device_remove() when the PCI/ACPI device is removed |
| - */ |
| - |
| -fw_trace_err: |
| -fw_run_err: |
| -fw_load_err: |
| -ipc_err: |
| -dbg_err: |
| |
| -#endif |
| + /* all resources freed, update state to match */ |
| + sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; |
| + sdev->first_boot = true; |
| |
| return ret; |
| } |
| @@ -545,10 +532,12 @@ int snd_sof_device_remove(struct device *dev) |
| if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) |
| cancel_work_sync(&sdev->probe_work); |
| |
| - snd_sof_fw_unload(sdev); |
| - snd_sof_ipc_free(sdev); |
| - snd_sof_free_debug(sdev); |
| - snd_sof_free_trace(sdev); |
| + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { |
| + snd_sof_fw_unload(sdev); |
| + snd_sof_ipc_free(sdev); |
| + snd_sof_free_debug(sdev); |
| + snd_sof_free_trace(sdev); |
| + } |
| |
| /* |
| * Unregister machine driver. This will unbind the snd_card which |
| @@ -564,7 +553,8 @@ int snd_sof_device_remove(struct device *dev) |
| * scheduled on, when they are unloaded. Therefore, the DSP must be |
| * removed only after the topology has been unloaded. |
| */ |
| - snd_sof_remove(sdev); |
| + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) |
| + snd_sof_remove(sdev); |
| |
| /* release firmware */ |
| release_firmware(pdata->fw); |
| diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c |
| index df51ecafd4b8..c9c7c4fd3a24 100644 |
| --- a/sound/soc/sof/pm.c |
| +++ b/sound/soc/sof/pm.c |
| @@ -260,6 +260,10 @@ static int sof_resume(struct device *dev, bool runtime_resume) |
| if (!sof_ops(sdev)->resume || !sof_ops(sdev)->runtime_resume) |
| return 0; |
| |
| + /* DSP was never successfully started, nothing to resume */ |
| + if (sdev->first_boot) |
| + return 0; |
| + |
| /* |
| * if the runtime_resume flag is set, call the runtime_resume routine |
| * or else call the system resume routine |
| -- |
| 2.7.4 |
| |