[PATCH 3/3] soc: samsung: exynos-pmu: fix error paths in cpuhotplug/idle states setup
Peter Griffin
peter.griffin at linaro.org
Wed Jun 10 06:34:21 PDT 2026
Hi Alexey,
Thanks for your patch!
On Fri, 5 Jun 2026 at 21:19, Alexey Klimov <alexey.klimov at linaro.org> wrote:
>
> 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.
I originally made the failure non-fatal because the system still boots
without the notifiers registered (and all other Arm64 Exynos SoCs
upstream don't register notifiers and AFAICT have broken cpu hotplug
and cpu idle).
In hindsight, that seems like a mistake. I think your patch to fully
unwind everything in case of failure makes more sense. See small
comment below about destroy_cpuhp_and_cpuidle()
>
> 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 at 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 at vger.kernel.org
> Signed-off-by: Alexey Klimov <alexey.klimov at linaro.org>
> ---
> drivers/soc/samsung/exynos-pmu.c | 57 ++++++++++++++++++++++++++++++++++------
> 1 file changed, 49 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
> index 9636287f6794..846313a28e9a 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;
> @@ -404,6 +406,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;
> @@ -465,16 +478,42 @@ 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;
>
> - cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/exynos-pmu:online",
> - NULL, gs101_cpuhp_pmu_offline);
> + 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;
> +
> + 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);
> + if (pmu_context->cpuhp_online_state != CPUHP_INVALID)
> + cpuhp_remove_state(pmu_context->cpuhp_online_state);
> +
> + return ret;
> }
>
> static int exynos_pmu_probe(struct platform_device *pdev)
> @@ -548,8 +587,10 @@ 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) {
> + destroy_cpuhp_and_cpuidle();
You only want to do this if pmu_cpuhp == true, as currently only gs101
registers the notifiers.
Thanks,
Peter
More information about the linux-arm-kernel
mailing list