[PATCH] clocksource/drivers/qcom: Fix error propagation in msm_timer_init()

From: Haotian Zhang

Date: Fri Oct 09 2026 - 01:28:33 EST


msm_timer_init() reuses the res variable for the clocksource_register_hz()
return value in its common exit path. When request_percpu_irq() or
cpuhp_setup_state() fails, res holds a negative error code, but it is
unconditionally overwritten by clocksource_register_hz() before the
function returns. As a successful clocksource registration returns 0, the
failure is reported as success: the percpu IRQ is not registered (or the
hotplug state is not installed) and yet msm_dt_timer_init() believes the
timer is usable. The same happens when alloc_percpu() fails, which also
leaves msm_evt NULL for the hotplug callbacks to dereference.

Return -ENOMEM when alloc_percpu() fails and bail out through a dedicated
error path that frees msm_evt and returns the original error code instead
of falling through to the clocksource registration.

Fixes: ab51189ca485 ("clocksource/drivers/qcom: Convert init function to return error")
Assisted-by: DeepSeek-V4.1-Flash
Signed-off-by: Haotian Zhang <vulab@xxxxxxxxxxx>
---
drivers/clocksource/timer-qcom.c | 34 +++++++++++++++++++-------------
1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/drivers/clocksource/timer-qcom.c b/drivers/clocksource/timer-qcom.c
index ddb1debe6a6b..d12c1c3dd6d8 100644
--- a/drivers/clocksource/timer-qcom.c
+++ b/drivers/clocksource/timer-qcom.c
@@ -160,28 +160,28 @@ static int __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
msm_evt = alloc_percpu(struct clock_event_device);
if (!msm_evt) {
pr_err("memory allocation failed for clockevents\n");
- goto err;
+ return -ENOMEM;
}

- if (percpu)
+ if (percpu) {
res = request_percpu_irq(irq, msm_timer_interrupt,
"gp_timer", msm_evt);
-
- if (res) {
- pr_err("request_percpu_irq failed\n");
- } else {
- /* Install and invoke hotplug callbacks */
- res = cpuhp_setup_state(CPUHP_AP_QCOM_TIMER_STARTING,
- "clockevents/qcom/timer:starting",
- msm_local_timer_starting_cpu,
- msm_local_timer_dying_cpu);
if (res) {
- free_percpu_irq(irq, msm_evt);
- goto err;
+ pr_err("request_percpu_irq failed\n");
+ goto err_free_evt;
}
}

-err:
+ /* Install and invoke hotplug callbacks */
+ res = cpuhp_setup_state(CPUHP_AP_QCOM_TIMER_STARTING,
+ "clockevents/qcom/timer:starting",
+ msm_local_timer_starting_cpu,
+ msm_local_timer_dying_cpu);
+ if (res) {
+ free_percpu_irq(irq, msm_evt);
+ goto err_free_evt;
+ }
+
writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
res = clocksource_register_hz(cs, dgt_hz);
if (res)
@@ -190,6 +190,12 @@ static int __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
msm_delay_timer.freq = dgt_hz;
register_current_timer_delay(&msm_delay_timer);

+ return res;
+
+err_free_evt:
+ free_percpu(msm_evt);
+ msm_evt = NULL;
+
return res;
}

--
2.25.1