From 75d90dab9875c92394a67f3d08c961fd20e67b64 Mon Sep 17 00:00:00 2001 From: Jie Gan Date: Mon, 7 Sep 2026 13:09:57 +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] Use pm_runtime_put_noidle() instead of pm_runtime_put() on the failure path so it drops the usage count without invoking runtime_suspend(), leaving the devm cleanup as the sole disabler. Affects catu, ctcu, etm4x, funnel, replicator, stm, tmc, tpiu and tnoc, all of which share this probe skeleton. Link: https://lore.kernel.org/all/20260907-fix-clk-issue-v1-1-efe81fa2b697@oss.qualcomm.com/ Fixes: 1abc1b212eff ("coresight: Appropriately disable programming clocks") Signed-off-by: Jie Gan --- drivers/hwtracing/coresight/coresight-catu.c | 9 ++++++--- drivers/hwtracing/coresight/coresight-ctcu-core.c | 9 ++++++--- drivers/hwtracing/coresight/coresight-etm4x-core.c | 10 ++++++---- drivers/hwtracing/coresight/coresight-funnel.c | 9 ++++++--- drivers/hwtracing/coresight/coresight-replicator.c | 9 ++++++--- drivers/hwtracing/coresight/coresight-stm.c | 9 ++++++--- drivers/hwtracing/coresight/coresight-tmc-core.c | 9 ++++++--- drivers/hwtracing/coresight/coresight-tnoc.c | 9 ++++++--- drivers/hwtracing/coresight/coresight-tpiu.c | 9 ++++++--- 9 files changed, 54 insertions(+), 28 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index a3ccb7034ae14..2b2493f694a31 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -634,11 +634,14 @@ 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_put_noidle(&pdev->dev); pm_runtime_disable(&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..09bef1e127408 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -319,11 +319,14 @@ 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_put_noidle(&pdev->dev); pm_runtime_disable(&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 fdda924a2c711..9339ee7665deb 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -2355,12 +2355,14 @@ 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_put_noidle(&pdev->dev); pm_runtime_disable(&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..71ca70868554e 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -477,11 +477,14 @@ 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_put_noidle(&pdev->dev); pm_runtime_disable(&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..651d7dd0023f0 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -530,11 +530,14 @@ 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_put_noidle(&pdev->dev); pm_runtime_disable(&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..c01a2ad1fab77 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -1013,11 +1013,14 @@ 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_put_noidle(&pdev->dev); pm_runtime_disable(&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..90b6e4c7858d1 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -1209,11 +1209,14 @@ 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_put_noidle(&pdev->dev); pm_runtime_disable(&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..7a8ff35d318cc 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -291,11 +291,14 @@ 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_put_noidle(&pdev->dev); pm_runtime_disable(&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..5a2db3382f75e 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -273,11 +273,14 @@ 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_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); + return ret; + } - return ret; + pm_runtime_put(&pdev->dev); + return 0; } static void tpiu_platform_remove(struct platform_device *pdev) From 43e1db93dfdea0f4b0e2fa910bfed3d5e74848ec 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 2b2493f694a31..76032347c495a 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -651,8 +651,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 09bef1e127408..c354ddad10807 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -336,8 +336,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 9339ee7665deb..17f76210107a8 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -2452,9 +2452,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 71ca70868554e..38f9b3b9871d5 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -494,8 +494,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 651d7dd0023f0..70e902c8036e1 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -547,8 +547,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 c01a2ad1fab77..8a7f7369b08b4 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -1030,8 +1030,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 90b6e4c7858d1..9f06a1b7f37c9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -1225,8 +1225,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 5a2db3382f75e..26306a2e24b9c 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -290,8 +290,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 3226736e6d3fb1fbb13b7ad1113a43c99741ba9b 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 7a8ff35d318cc..5ea6e55ff17c4 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -305,8 +305,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); }