[PATCH v2 2/2] soc: samsung: exynos-pmu: fix error paths in cpuhotplug/idle states setup
From: Alexey Klimov <alexey.klimov@linaro.org>
Date: 2026-08-28 05:37:17
Also in:
linux-samsung-soc, lkml, stable
Subsystem:
arm/samsung s3c, s5p and exynos arm architectures, the rest · Maintainers:
Krzysztof Kozlowski, Peter Griffin, Linus Torvalds
The setup_cpuhp_and_cpuidle() initialisation sequence currently ignores the return values of cpuhp_setup_state(), cpu_pm_register_notifier(), and register_reboot_notifier(). If any of these registrations fail during probe() routine, the driver returns 0, leaving the driver partially configured. Furthermore, if anything after setup_cpuhp_and_cpuidle() fails in probe() routine, for instance devm_mfd_add_devices(), the probe() lacks an error path and leaves notifiers and cpu hotplug states registered. Introduce variables for the cpu hotplug state IDs in exynos_pmu_context struct, that should be initialised to CPUHP_INVALID by default. Check all return codes in setup_cpuhp_and_cpuidle(), and add an error path to remove registered states on failure. Finally, add destroy_cpuhp_and_cpuidle() helper to safely tear down notifiers and cpu hotplug states. Reported-by: Sashiko <sashiko-bot@kernel.org> Closes: https://sashiko.dev/#/patchset/20260513-exynos850-cpuhotplug-v4-0-54fec5f65362@linaro.org?part=3 Fixes: 78b72897a5c8 ("soc: samsung: exynos-pmu: Enable CPU Idle for gs101") Cc: stable@vger.kernel.org Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org> --- drivers/soc/samsung/exynos-pmu.c | 63 +++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-)
diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
index efccdd63e40e..fce922d5ab92 100644
--- a/drivers/soc/samsung/exynos-pmu.c
+++ b/drivers/soc/samsung/exynos-pmu.c@@ -38,6 +38,8 @@ struct exynos_pmu_context { unsigned long *in_cpuhp; bool sys_insuspend; bool sys_inreboot; + int cpuhp_prepare_state; + int cpuhp_online_state; }; void __iomem *pmu_base_addr;
@@ -407,6 +409,17 @@ static struct notifier_block exynos_cpupm_reboot_nb = { .notifier_call = exynos_cpupm_reboot_notifier, }; +static void destroy_cpuhp_and_cpuidle(void) +{ + cpu_pm_unregister_notifier(&gs101_cpu_pm_notifier); + unregister_reboot_notifier(&exynos_cpupm_reboot_nb); + + if (pmu_context->cpuhp_prepare_state != CPUHP_INVALID) + cpuhp_remove_state(pmu_context->cpuhp_prepare_state); + if (pmu_context->cpuhp_online_state != CPUHP_INVALID) + cpuhp_remove_state(pmu_context->cpuhp_online_state); +} + static int setup_cpuhp_and_cpuidle(struct device *dev) { struct device_node *intr_gen_node __free(device_node) =
@@ -458,16 +471,46 @@ static int setup_cpuhp_and_cpuidle(struct device *dev) gs101_cpuhp_pmu_online(cpu); /* register CPU hotplug callbacks */ - cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "soc/exynos-pmu:prepare", - gs101_cpuhp_pmu_online, NULL); + pmu_context->cpuhp_prepare_state = CPUHP_INVALID; + pmu_context->cpuhp_online_state = CPUHP_INVALID; + + ret = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "soc/exynos-pmu:prepare", + gs101_cpuhp_pmu_online, NULL); + if (ret < 0) + return ret; + + pmu_context->cpuhp_prepare_state = ret; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/exynos-pmu:online", + NULL, gs101_cpuhp_pmu_offline); + if (ret < 0) + goto clean_cpuhp_states; - cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/exynos-pmu:online", - NULL, gs101_cpuhp_pmu_offline); + pmu_context->cpuhp_online_state = ret; /* register CPU PM notifiers for cpuidle */ - cpu_pm_register_notifier(&gs101_cpu_pm_notifier); - register_reboot_notifier(&exynos_cpupm_reboot_nb); - return 0; + ret = cpu_pm_register_notifier(&gs101_cpu_pm_notifier); + if (ret) + goto clean_cpuhp_states; + + ret = register_reboot_notifier(&exynos_cpupm_reboot_nb); + if (!ret) + /* Success */ + return ret; + + cpu_pm_unregister_notifier(&gs101_cpu_pm_notifier); + +clean_cpuhp_states: + if (pmu_context->cpuhp_prepare_state != CPUHP_INVALID) { + cpuhp_remove_state(pmu_context->cpuhp_prepare_state); + pmu_context->cpuhp_prepare_state = CPUHP_INVALID; + } + if (pmu_context->cpuhp_online_state != CPUHP_INVALID) { + cpuhp_remove_state(pmu_context->cpuhp_online_state); + pmu_context->cpuhp_online_state = CPUHP_INVALID; + } + + return ret; } static int exynos_pmu_probe(struct platform_device *pdev)
@@ -541,8 +584,12 @@ static int exynos_pmu_probe(struct platform_device *pdev) ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, exynos_pmu_devs, ARRAY_SIZE(exynos_pmu_devs), NULL, 0, NULL); - if (ret) + if (ret) { + if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_cpuhp) + destroy_cpuhp_and_cpuidle(); + return ret; + } if (devm_of_platform_populate(dev)) dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n");
--
2.51.0