From 76a6b8645ec28b207257d670df5cdc4187cbf76d Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Thu, 10 Sep 2026 09:17:05 +0800 Subject: [PATCH 1/3] FROMLIST: coresight: Fix clock refcount imbalance on platform probe failure Each CoreSight platform_driver's probe() enables its clocks via coresight_get_enable_clocks() -> devm_clk_get_optional_enabled(), which registers a devm cleanup to run clk_disable_unprepare() on driver detach. The probe wrapper then unconditionally calls pm_runtime_put() regardless of whether the inner probe succeeded, so on failure this also fires runtime_suspend() and disables the same clocks a first time. The driver core then unwinds the failed probe and runs the devm cleanup, disabling them a second time and underflowing the refcount: coresight-etm4x etm0: probe with driver coresight-etm4x failed with error -22 ------------[ cut here ]------------ qdss_clk already disabled WARNING: CPU: 1 PID: 432 at drivers/clk/clk.c:1188 clk_core_disable+0x1d0/0x218 ... ------------[ cut here ]------------ Unpreparing enabled qdss_clk WARNING: CPU: 0 PID: 432 at drivers/clk/clk.c:1061 clk_core_unprepare+0x248/0x268 ... qdss_clk is shared by every CoreSight node, so the extra disable drives its refcount to 0 while sibling devices still expect it enabled. The next funnel to probe then touches unclocked hardware and panics: SError Interrupt on CPU1, code 0x00000000be000000 -- SError Kernel panic - not syncing: Asynchronous SError Interrupt ... coresight_clear_self_claim_tag+0x7c/0x1e0 [coresight] (P) funnel_probe+0x114/0x2e0 [coresight_funnel] dynamic_funnel_probe+0x24/0x70 [coresight_funnel] On the failure path, disable runtime PM before releasing the usage reference: call pm_runtime_disable() first so no further runtime_suspend() can be scheduled, then pm_runtime_set_suspended() to explicitly record the suspended state, and finally pm_runtime_put_noidle() to drop the reference without invoking runtime_suspend(), leaving the devm cleanup as the sole disabler of the clocks. Affects catu, ctcu, etm4x, funnel, replicator, stm, tmc, tpiu and tnoc, all of which share this probe skeleton. Link: https://lore.kernel.org/all/20260910-fix-clk-issue-v2-1-fda3dd324c0b@oss.qualcomm.com/ Reviewed-by: Leo Yan Fixes: 1abc1b212eff ("coresight: Appropriately disable programming clocks") Signed-off-by: Jie Gan --- drivers/hwtracing/coresight/coresight-catu.c | 10 +++++++--- drivers/hwtracing/coresight/coresight-ctcu-core.c | 10 +++++++--- drivers/hwtracing/coresight/coresight-etm4x-core.c | 11 +++++++---- drivers/hwtracing/coresight/coresight-funnel.c | 10 +++++++--- drivers/hwtracing/coresight/coresight-replicator.c | 10 +++++++--- drivers/hwtracing/coresight/coresight-stm.c | 10 +++++++--- drivers/hwtracing/coresight/coresight-tmc-core.c | 10 +++++++--- drivers/hwtracing/coresight/coresight-tnoc.c | 10 +++++++--- drivers/hwtracing/coresight/coresight-tpiu.c | 10 +++++++--- 9 files changed, 63 insertions(+), 28 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index a3ccb7034ae14..469c0b45bacea 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -634,11 +634,15 @@ static int catu_platform_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = __catu_probe(&pdev->dev, res); - pm_runtime_put(&pdev->dev); - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static void catu_platform_remove(struct platform_device *pdev) diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c index 46e71319adc79..a03715325539c 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -319,11 +319,15 @@ static int ctcu_platform_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = ctcu_probe(pdev); - pm_runtime_put(&pdev->dev); - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static void ctcu_platform_remove(struct platform_device *pdev) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 2271f5ea81214..a52e1848566bb 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -2377,12 +2377,15 @@ static int etm4_probe_platform_dev(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = etm4_probe(&pdev->dev); - - pm_runtime_put(&pdev->dev); - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static int etm4_probe_cpu(unsigned int cpu) diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index fd8dcd541454b..aeb697b69eff0 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -477,11 +477,15 @@ static int funnel_platform_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = funnel_probe(&pdev->dev, res); - pm_runtime_put(&pdev->dev); - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static void funnel_platform_remove(struct platform_device *pdev) diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 6cb57763f9b10..a4e66d7b785d5 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -530,11 +530,15 @@ static int replicator_platform_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = replicator_probe(&pdev->dev, res); - pm_runtime_put(&pdev->dev); - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static void replicator_platform_remove(struct platform_device *pdev) diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index e68529bf89c98..de282c6e85cf8 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -1013,11 +1013,15 @@ static int stm_platform_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = __stm_probe(&pdev->dev, res); - pm_runtime_put(&pdev->dev); - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static void stm_platform_remove(struct platform_device *pdev) diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 984be10c10fd2..617fc5d6930d0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -1209,11 +1209,15 @@ static int tmc_platform_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = __tmc_probe(&pdev->dev, res); - pm_runtime_put(&pdev->dev); - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static void tmc_platform_remove(struct platform_device *pdev) diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c index e832233420592..0462f65d1e890 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -291,11 +291,15 @@ static int tnoc_platform_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = _tnoc_probe(&pdev->dev, res); - pm_runtime_put(&pdev->dev); - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static void tnoc_platform_remove(struct platform_device *pdev) diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 9463afdbda8ad..f4ef676e0ca38 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -273,11 +273,15 @@ static int tpiu_platform_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = __tpiu_probe(&pdev->dev, res); - pm_runtime_put(&pdev->dev); - if (ret) + if (ret) { pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static void tpiu_platform_remove(struct platform_device *pdev) From eb34a42e23a11096ef8e2c8a466d4576d2a40e84 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Fri, 10 Jul 2026 09:14:47 +0800 Subject: [PATCH 2/3] FROMLIST: coresight: Fix clock refcount imbalance on platform remove coresight_get_enable_clocks() enables the programming clock and the optional AT clock through devm_clk_get_optional_enabled(), which also registers a devm action to call clk_disable_unprepare() when the driver detaches. After probe, pm_runtime_put() allows the device to suspend and the runtime suspend callback disables the same clocks. During remove the device is left runtime suspended, so pm_runtime_disable() freezes it with the clocks already disabled. The devm cleanup that runs afterwards calls clk_disable_unprepare() a second time, underflowing the clock enable refcount. Resume the device with pm_runtime_get_sync() before tearing it down so the clocks are enabled again and balance the devm-managed disable. Then pm_runtime_set_suspended() and pm_runtime_put_noidle() leave the device in a coherent runtime PM state (suspended, usage count balanced) once the devm action has disabled the clocks. This affects all CoreSight platform drivers that obtain their clocks through coresight_get_enable_clocks(): catu, cpu-debug, ctcu, etm4x, funnel, replicator, stm, tmc and tpiu. Link: https://lore.kernel.org/all/20260710-fix-clock-refcount-unbalance-v3-1-a37a1fb17981@oss.qualcomm.com/ Fixes: 1abc1b212eff ("coresight: Appropriately disable programming clocks") Reviewed-by: Yeoreum Yun Reviewed-by: Leo Yan Signed-off-by: Jie Gan --- drivers/hwtracing/coresight/coresight-catu.c | 8 ++++++++ drivers/hwtracing/coresight/coresight-cpu-debug.c | 8 ++++++++ drivers/hwtracing/coresight/coresight-ctcu-core.c | 8 ++++++++ drivers/hwtracing/coresight/coresight-etm4x-core.c | 13 +++++++++++-- drivers/hwtracing/coresight/coresight-funnel.c | 8 ++++++++ drivers/hwtracing/coresight/coresight-replicator.c | 8 ++++++++ drivers/hwtracing/coresight/coresight-stm.c | 8 ++++++++ drivers/hwtracing/coresight/coresight-tmc-core.c | 9 +++++++++ drivers/hwtracing/coresight/coresight-tpiu.c | 8 ++++++++ 9 files changed, 76 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index 469c0b45bacea..536670f2245a8 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -652,8 +652,16 @@ static void catu_platform_remove(struct platform_device *pdev) if (WARN_ON(!drvdata)) return; + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); __catu_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_PM diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index 5f21366406aae..acc37e8541bc9 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c @@ -714,8 +714,16 @@ static void debug_platform_remove(struct platform_device *pdev) if (WARN_ON(!drvdata)) return; + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); __debug_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_ACPI diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c index a03715325539c..3cf668845beca 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -337,8 +337,16 @@ static void ctcu_platform_remove(struct platform_device *pdev) if (WARN_ON(!drvdata)) return; + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); ctcu_remove(pdev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_PM diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index a52e1848566bb..2ac48e57ef441 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -2475,9 +2475,18 @@ static void etm4_remove_platform_dev(struct platform_device *pdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(&pdev->dev); - if (drvdata) - etm4_remove_dev(drvdata); + if (WARN_ON(!drvdata)) + return; + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); + etm4_remove_dev(drvdata); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } static const struct amba_id etm4_ids[] = { diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index aeb697b69eff0..0c6ba9f63bfb6 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -495,8 +495,16 @@ static void funnel_platform_remove(struct platform_device *pdev) if (WARN_ON(!drvdata)) return; + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); funnel_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } static const struct of_device_id funnel_match[] = { diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index a4e66d7b785d5..6eb499eea16d6 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -548,8 +548,16 @@ static void replicator_platform_remove(struct platform_device *pdev) if (WARN_ON(!drvdata)) return; + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); replicator_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_PM diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index de282c6e85cf8..b085b4399f753 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -1031,8 +1031,16 @@ static void stm_platform_remove(struct platform_device *pdev) if (WARN_ON(!drvdata)) return; + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); __stm_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_ACPI diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 617fc5d6930d0..f50b084b480c3 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -1226,8 +1226,17 @@ static void tmc_platform_remove(struct platform_device *pdev) if (WARN_ON(!drvdata)) return; + + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); __tmc_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_PM diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index f4ef676e0ca38..0b59d923da4f0 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -291,8 +291,16 @@ static void tpiu_platform_remove(struct platform_device *pdev) if (WARN_ON(!drvdata)) return; + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); __tpiu_remove(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_ACPI From cd97d616475a5ffde3348dbc00f7915ccaf353a2 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Fri, 10 Jul 2026 09:14:48 +0800 Subject: [PATCH 3/3] FROMLIST: coresight: tnoc: Fix clock refcount imbalance on platform remove coresight_get_enable_clocks() enables the programming clock through devm_clk_get_optional_enabled(), which also registers a devm action to call clk_disable_unprepare() when the driver detaches. After probe, pm_runtime_put() allows the device to suspend and the runtime suspend callback disables the clock. During remove the device is left runtime suspended, so pm_runtime_disable() freezes it with the clock already disabled. The devm cleanup that runs afterwards calls clk_disable_unprepare() a second time, underflowing the clock enable refcount. Resume the device with pm_runtime_get_sync() before tearing it down so the clock is enabled again and balances the devm-managed disable. Then pm_runtime_set_suspended() and pm_runtime_put_noidle() leave the device in a coherent runtime PM state (suspended, usage count balanced) once the devm action has disabled the clock. Link: https://lore.kernel.org/all/20260710-fix-clock-refcount-unbalance-v3-2-a37a1fb17981@oss.qualcomm.com/ Fixes: 1abc1b212eff ("coresight: Appropriately disable programming clocks") Reviewed-by: Leo Yan Signed-off-by: Jie Gan --- drivers/hwtracing/coresight/coresight-tnoc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c index 0462f65d1e890..24c22d681f386 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -306,8 +306,16 @@ static void tnoc_platform_remove(struct platform_device *pdev) { struct trace_noc_drvdata *drvdata = platform_get_drvdata(pdev); + /* + * Resume the device so its clocks are enabled again, balancing the + * clk_disable_unprepare() that devm runs when the driver detaches. + * Then mark it suspended and drop the usage count taken here. + */ + pm_runtime_get_sync(&pdev->dev); coresight_unregister(drvdata->csdev); pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); if (drvdata->atid > 0) coresight_trace_id_put_system_id(drvdata->atid); }