This series focuses on CoreSight path power management. The changes can be divided into four parts for review:
Patches 01~06: Refactor the CPU idle flow with moving common code into the CoreSight core layer. Patches 07~14: Add link control during CPU idle. Patches 15~16: Support the sink (TRBE) control during CPU idle. Patches 17~19: Move the CPU hotplug flow into the coresight core layer and simplify the code.
This series is rebased on the coresight-next branch and has been verified on Juno-r2 and FVP RevC.
--- Changes in v6: - Rebase on the latest coresight-next branch. - Always save and restore TRBE context during idle (Will). - Use get_cpu() / put_cpu() when set the per CPU source pointer. - Link to v5: https://lore.kernel.org/r/20251119-arm_coresight_path_power_management_impro...
Changes in v5: - Set the per-CPU source pointer on target CPU (Suzuki). - Reused existed enable/disable buffer functions in TRBE callbacks (James). - Refactored refcount for source devices in SysFS mode. - Released path in cpu-hotplug off flow to avoid memory leak. - Updated ETMv3 driver when move common code into core layer. - Rebased on the latest coresight-next branch. - Link to v4: https://lore.kernel.org/r/20251104-arm_coresight_path_power_management_impro...
Changes in v4: - Changed to store path pointer in coresight_device, this is easier for fetching path pointer based on source device (Mike). - Dropped changes in CTI driver. - Only disabled path for CPU hot-plugged off but not enable path for hot-plugged in. - Removed James' test tags for modified patches. - Link to v3: https://lore.kernel.org/r/20250915-arm_coresight_power_management_fix-v3-0-e...
Signed-off-by: Leo Yan leo.yan@arm.com
--- Leo Yan (18): coresight: sysfs: Validate CPU online status for per-CPU sources coresight: Set per-CPU source pointer coresight: Register CPU PM notifier in core layer coresight: etm4x: Hook CPU PM callbacks coresight: Add callback to determine if PM is needed coresight: etm4x: Remove redundant condition checks in save and restore coresight: syscfg: Use spinlock to protect active variables coresight: Introduce coresight_enable_source() helper coresight: Save active path for system tracers coresight: etm4x: Set active path on target CPU coresight: etm3x: Set active path on target CPU coresight: sysfs: Use source's path pointer for path control coresight: Add 'in_idle' argument to path coresight: Control path during CPU idle coresight: Add PM callbacks for sink device coresight: sysfs: Increment refcount only for system tracers coresight: Take hotplug lock in enable_source_store() for Sysfs mode coresight: Move CPU hotplug callbacks to core layer
Yabin Cui (1): coresight: trbe: Save and restore state across CPU low power state
drivers/hwtracing/coresight/coresight-catu.c | 1 + drivers/hwtracing/coresight/coresight-core.c | 273 ++++++++++++++++++++- drivers/hwtracing/coresight/coresight-ctcu-core.c | 1 + drivers/hwtracing/coresight/coresight-cti-core.c | 1 + drivers/hwtracing/coresight/coresight-dummy.c | 1 + drivers/hwtracing/coresight/coresight-etb10.c | 1 + drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +- drivers/hwtracing/coresight/coresight-etm3x-core.c | 65 ++--- drivers/hwtracing/coresight/coresight-etm4x-core.c | 153 +++--------- drivers/hwtracing/coresight/coresight-funnel.c | 1 + drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight-replicator.c | 1 + drivers/hwtracing/coresight/coresight-stm.c | 1 + drivers/hwtracing/coresight/coresight-syscfg.c | 22 +- drivers/hwtracing/coresight/coresight-syscfg.h | 2 + drivers/hwtracing/coresight/coresight-sysfs.c | 126 +++------- drivers/hwtracing/coresight/coresight-tmc-core.c | 1 + drivers/hwtracing/coresight/coresight-tnoc.c | 2 + drivers/hwtracing/coresight/coresight-tpda.c | 1 + drivers/hwtracing/coresight/coresight-tpdm.c | 1 + drivers/hwtracing/coresight/coresight-tpiu.c | 1 + drivers/hwtracing/coresight/coresight-trbe.c | 60 ++++- drivers/hwtracing/coresight/ultrasoc-smb.c | 1 + include/linux/coresight.h | 11 + 24 files changed, 458 insertions(+), 274 deletions(-) --- base-commit: 9c5ef7a30d9044f8706bd02bfdc4eff7266f3e25 change-id: 20251104-arm_coresight_path_power_management_improvement-dab4966f8280
Best regards,
The current SysFS flow first enables the links and sink, then rolls back to disable them if the source fails to enable. This failure can occur if the associated CPU is offline, which causes the SMP call to fail.
Populate CPU ID into the coresight_device structure, for components that are not CPU bound, set this field to -1.
Validate whether the associated CPU is online for a per-CPU tracer. If the CPU is offline, return -ENODEV and bail out.
Reviewed-by: Yeoreum Yun yeoreum.yun@arm.com Reviewed-by: Mike Leach mike.leach@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-catu.c | 1 + drivers/hwtracing/coresight/coresight-core.c | 1 + drivers/hwtracing/coresight/coresight-ctcu-core.c | 1 + drivers/hwtracing/coresight/coresight-cti-core.c | 1 + drivers/hwtracing/coresight/coresight-dummy.c | 1 + drivers/hwtracing/coresight/coresight-etb10.c | 1 + drivers/hwtracing/coresight/coresight-etm3x-core.c | 1 + drivers/hwtracing/coresight/coresight-etm4x-core.c | 1 + drivers/hwtracing/coresight/coresight-funnel.c | 1 + drivers/hwtracing/coresight/coresight-replicator.c | 1 + drivers/hwtracing/coresight/coresight-stm.c | 1 + drivers/hwtracing/coresight/coresight-sysfs.c | 3 +++ drivers/hwtracing/coresight/coresight-tmc-core.c | 1 + drivers/hwtracing/coresight/coresight-tnoc.c | 2 ++ drivers/hwtracing/coresight/coresight-tpda.c | 1 + drivers/hwtracing/coresight/coresight-tpdm.c | 1 + drivers/hwtracing/coresight/coresight-tpiu.c | 1 + drivers/hwtracing/coresight/coresight-trbe.c | 1 + drivers/hwtracing/coresight/ultrasoc-smb.c | 1 + include/linux/coresight.h | 4 ++++ 20 files changed, 26 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index ce71dcddfca2558eddd625de58a709b151f2e07e..8f22f12e260787335cb71b06ba7c8aeccf603fc8 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -573,6 +573,7 @@ static int __catu_probe(struct device *dev, struct resource *res) catu_desc.type = CORESIGHT_DEV_TYPE_HELPER; catu_desc.subtype.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CATU; catu_desc.ops = &catu_ops; + catu_desc.cpu = -1;
coresight_clear_self_claim_tag(&catu_desc.access); drvdata->csdev = coresight_register(&catu_desc); diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 46f247f73cf64a97b9353b84ba5b76b991676f5f..f191a0fe802ac6ae1342872e01fff463775c8a20 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1341,6 +1341,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->ops = desc->ops; csdev->access = desc->access; csdev->orphan = true; + csdev->cpu = desc->cpu;
csdev->dev.type = &coresight_dev_type[desc->type]; csdev->dev.groups = desc->groups; diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c index 9043cad42f01e6d1facb07a1f85623250535ed9c..58113cc1add3fd37afec1b5bc11eb26c0f95b339 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -226,6 +226,7 @@ static int ctcu_probe(struct platform_device *pdev) desc.dev = dev; desc.ops = &ctcu_ops; desc.access = CSDEV_ACCESS_IOMEM(base); + desc.cpu = -1; raw_spin_lock_init(&drvdata->spin_lock);
drvdata->csdev = coresight_register(&desc); diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index 2f4c9362709a90b12a1aeb5016905b7d4474b912..5f4ff84f20912a3f8405448d028c742a7c860d26 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -725,6 +725,7 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) cti_desc.ops = &cti_ops; cti_desc.groups = drvdata->ctidev.con_groups; cti_desc.dev = dev; + cti_desc.cpu = drvdata->ctidev.cpu;
coresight_clear_self_claim_tag(&cti_desc.access); drvdata->csdev = coresight_register(&cti_desc); diff --git a/drivers/hwtracing/coresight/coresight-dummy.c b/drivers/hwtracing/coresight/coresight-dummy.c index c176a2f57300fd8cd5d153be5ccb6faaef96023d..788fb28c74a901382a28326ba230455125a471ee 100644 --- a/drivers/hwtracing/coresight/coresight-dummy.c +++ b/drivers/hwtracing/coresight/coresight-dummy.c @@ -176,6 +176,7 @@ static int dummy_probe(struct platform_device *pdev)
desc.pdata = pdev->dev.platform_data; desc.dev = &pdev->dev; + desc.cpu = -1; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index b952a1d47f12f44c15cc845914691305713028c4..b8df9e3c93df167dabd39d90b6f9491def0f6b90 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -768,6 +768,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_etb_groups; + desc.cpu = -1;
coresight_clear_self_claim_tag(&desc.access); drvdata->csdev = coresight_register(&desc); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index a547a6d2e0bde84748f753e5529d316c4f5e82e2..2e9e824309bdab5c2983ed1a3e61b27c9d822278 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -891,6 +891,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_etm_groups; + desc.cpu = drvdata->cpu; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index d565a73f0042e3e0b21fcf9cb94009cc25834d3d..add4cc9c1eca42b58d8f9d7bb3dc8d2ea1241ee2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -2260,6 +2260,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_etmv4_groups; + desc.cpu = drvdata->cpu; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 3f56ceccd8c9f6deaaf783a06a0d19fb06eff7c4..c281c36a84015a73023ba9db2eae29ce13c6e880 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -261,6 +261,7 @@ static int funnel_probe(struct device *dev, struct resource *res) desc.ops = &funnel_cs_ops; desc.pdata = pdata; desc.dev = dev; + desc.cpu = -1; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 07fc04f53b88fdec7ae18d3000046fe74c0cec9d..e947e8f0e8c4bfc4d85da385663ea3805a9b1df1 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -271,6 +271,7 @@ static int replicator_probe(struct device *dev, struct resource *res) desc.ops = &replicator_cs_ops; desc.pdata = dev->platform_data; desc.dev = dev; + desc.cpu = -1;
drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index aca6cec7885a551ad5960986b1cdf08baa6b5a94..d60fdec640c760c4648ece8d28636936a1b9fb9b 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -899,6 +899,7 @@ static int __stm_probe(struct device *dev, struct resource *res) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_stm_groups; + desc.cpu = -1; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index d2a6ed8bcc74d64dccc735463f14790b4e80d101..7c7d3a3cb9f18a9dfa78eb5c19dda43fa6656271 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -162,6 +162,9 @@ static int coresight_validate_source_sysfs(struct coresight_device *csdev, return -EINVAL; }
+ if (coresight_is_percpu_source(csdev) && !cpu_online(csdev->cpu)) + return -ENODEV; + return 0; }
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index c89fe996af23194c7e59432ce39ce20bb65937cf..5af06ae8b44d0e53a479822f25ab58e692a9696b 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -863,6 +863,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) } dev->platform_data = pdata; desc.pdata = pdata; + desc.cpu = -1;
coresight_clear_self_claim_tag(&desc.access); drvdata->csdev = coresight_register(&desc); diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c index 96a25877b8240f20921efa979a361eb473c05600..c8530c625c0b04699e82b602e556718717a239f7 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -226,6 +226,8 @@ static int _tnoc_probe(struct device *dev, struct resource *res) desc.dev = dev; desc.access = CSDEV_ACCESS_IOMEM(drvdata->base); desc.groups = coresight_tnoc_groups; + desc.cpu = -1; + drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { if (drvdata->atid > 0) diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index 89c8f71f0aff06e87971976efb23feb0057fcce6..b96dfc697b1d881b853839dce196e3024f489fc3 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -593,6 +593,7 @@ static int tpda_probe(struct amba_device *adev, const struct amba_id *id) desc.dev = &adev->dev; desc.groups = tpda_attr_grps; desc.access = CSDEV_ACCESS_IOMEM(base); + desc.cpu = -1; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index da77bdaad0a4519bd4e0653cc92e2a333e59e60d..b61cc260ec0b043a96772a47ca0dd536ef12e290 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -1423,6 +1423,7 @@ static int tpdm_probe(struct device *dev, struct resource *res) desc.pdata = dev->platform_data; desc.dev = dev; desc.access = CSDEV_ACCESS_IOMEM(base); + desc.cpu = -1; if (res) desc.groups = tpdm_attr_grps; drvdata->csdev = coresight_register(&desc); diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index b8560b140e0fa4bf8bf1e5c60fe67077b11798fd..a5254f36820b9e75047241d8690ff39b0f62b143 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -169,6 +169,7 @@ static int __tpiu_probe(struct device *dev, struct resource *res) desc.ops = &tpiu_cs_ops; desc.pdata = pdata; desc.dev = dev; + desc.cpu = -1; drvdata->csdev = coresight_register(&desc);
if (!IS_ERR(drvdata->csdev)) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 1511f8eb95afb5b4610b8fbdacc8b174b6b08530..fc6e3551b7002634bcf6a154080a142dfe32c890 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -1289,6 +1289,7 @@ static void arm_trbe_register_coresight_cpu(struct trbe_drvdata *drvdata, int cp desc.ops = &arm_trbe_cs_ops; desc.groups = arm_trbe_groups; desc.dev = dev; + desc.cpu = cpu; trbe_csdev = coresight_register(&desc); if (IS_ERR(trbe_csdev)) goto cpu_clear; diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 5776f63468fa05df255d3dd716f621c37424c048..5217a9930e2632a40ba0523438052872cea62aea 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -482,6 +482,7 @@ static int smb_register_sink(struct platform_device *pdev, return -ENOMEM; } desc.access = CSDEV_ACCESS_IOMEM(drvdata->base); + desc.cpu = -1;
drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 2131febebee93d609df1aea8534a10898b600be2..6810acc8533e0eb0c5d41a7fbe0bbd03eb94057d 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -153,6 +153,7 @@ struct csdev_access { * in the component's sysfs sub-directory. * @name: name for the coresight device, also shown under sysfs. * @access: Describe access to the device + * @cpu: The CPU this component is affined to (-1 for not CPU bound). */ struct coresight_desc { enum coresight_dev_type type; @@ -163,6 +164,7 @@ struct coresight_desc { const struct attribute_group **groups; const char *name; struct csdev_access access; + int cpu; };
/** @@ -261,6 +263,7 @@ struct coresight_trace_id_map { * CS_MODE_SYSFS. Otherwise it must be accessed from inside the * spinlock. * @orphan: true if the component has connections that haven't been linked. + * @cpu: The CPU this component is affined to (-1 for not CPU bound). * @sysfs_sink_activated: 'true' when a sink has been selected for use via sysfs * by writing a 1 to the 'enable_sink' file. A sink can be * activated but not yet enabled. Enabling for a _sink_ happens @@ -287,6 +290,7 @@ struct coresight_device { atomic_t mode; int refcnt; bool orphan; + int cpu; /* sink specific fields */ bool sysfs_sink_activated; struct dev_ext_attribute *ea;
On 05/03/2026 10:17, Leo Yan wrote:
The current SysFS flow first enables the links and sink, then rolls back to disable them if the source fails to enable. This failure can occur if the associated CPU is offline, which causes the SMP call to fail.
Populate CPU ID into the coresight_device structure, for components that are not CPU bound, set this field to -1.
Validate whether the associated CPU is online for a per-CPU tracer. If the CPU is offline, return -ENODEV and bail out.
Reviewed-by: Yeoreum Yun yeoreum.yun@arm.com Reviewed-by: Mike Leach mike.leach@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com
While this works, how about we make it an explicit buy in from the driver for CPU bound ?
e.g., add a flag in the coresight_desc.flags = CORESIGHT_DESC_CPU_BOUND
and then desc.cpu describes the associated CPU. Otherwise, defaults to -1. That way you could restrict the changes to only those that need the CPU (ETMx and CTIs ?) Also makes any new driver safe.
Cheers Suzuki
drivers/hwtracing/coresight/coresight-catu.c | 1 + drivers/hwtracing/coresight/coresight-core.c | 1 + drivers/hwtracing/coresight/coresight-ctcu-core.c | 1 + drivers/hwtracing/coresight/coresight-cti-core.c | 1 + drivers/hwtracing/coresight/coresight-dummy.c | 1 + drivers/hwtracing/coresight/coresight-etb10.c | 1 + drivers/hwtracing/coresight/coresight-etm3x-core.c | 1 + drivers/hwtracing/coresight/coresight-etm4x-core.c | 1 + drivers/hwtracing/coresight/coresight-funnel.c | 1 + drivers/hwtracing/coresight/coresight-replicator.c | 1 + drivers/hwtracing/coresight/coresight-stm.c | 1 + drivers/hwtracing/coresight/coresight-sysfs.c | 3 +++ drivers/hwtracing/coresight/coresight-tmc-core.c | 1 + drivers/hwtracing/coresight/coresight-tnoc.c | 2 ++ drivers/hwtracing/coresight/coresight-tpda.c | 1 + drivers/hwtracing/coresight/coresight-tpdm.c | 1 + drivers/hwtracing/coresight/coresight-tpiu.c | 1 + drivers/hwtracing/coresight/coresight-trbe.c | 1 + drivers/hwtracing/coresight/ultrasoc-smb.c | 1 + include/linux/coresight.h | 4 ++++ 20 files changed, 26 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index ce71dcddfca2558eddd625de58a709b151f2e07e..8f22f12e260787335cb71b06ba7c8aeccf603fc8 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -573,6 +573,7 @@ static int __catu_probe(struct device *dev, struct resource *res) catu_desc.type = CORESIGHT_DEV_TYPE_HELPER; catu_desc.subtype.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CATU; catu_desc.ops = &catu_ops;
- catu_desc.cpu = -1;
coresight_clear_self_claim_tag(&catu_desc.access); drvdata->csdev = coresight_register(&catu_desc); diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 46f247f73cf64a97b9353b84ba5b76b991676f5f..f191a0fe802ac6ae1342872e01fff463775c8a20 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1341,6 +1341,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->ops = desc->ops; csdev->access = desc->access; csdev->orphan = true;
- csdev->cpu = desc->cpu;
csdev->dev.type = &coresight_dev_type[desc->type]; csdev->dev.groups = desc->groups; diff --git a/drivers/hwtracing/coresight/coresight-ctcu-core.c b/drivers/hwtracing/coresight/coresight-ctcu-core.c index 9043cad42f01e6d1facb07a1f85623250535ed9c..58113cc1add3fd37afec1b5bc11eb26c0f95b339 100644 --- a/drivers/hwtracing/coresight/coresight-ctcu-core.c +++ b/drivers/hwtracing/coresight/coresight-ctcu-core.c @@ -226,6 +226,7 @@ static int ctcu_probe(struct platform_device *pdev) desc.dev = dev; desc.ops = &ctcu_ops; desc.access = CSDEV_ACCESS_IOMEM(base);
- desc.cpu = -1; raw_spin_lock_init(&drvdata->spin_lock);
drvdata->csdev = coresight_register(&desc); diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index 2f4c9362709a90b12a1aeb5016905b7d4474b912..5f4ff84f20912a3f8405448d028c742a7c860d26 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -725,6 +725,7 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) cti_desc.ops = &cti_ops; cti_desc.groups = drvdata->ctidev.con_groups; cti_desc.dev = dev;
- cti_desc.cpu = drvdata->ctidev.cpu;
coresight_clear_self_claim_tag(&cti_desc.access); drvdata->csdev = coresight_register(&cti_desc); diff --git a/drivers/hwtracing/coresight/coresight-dummy.c b/drivers/hwtracing/coresight/coresight-dummy.c index c176a2f57300fd8cd5d153be5ccb6faaef96023d..788fb28c74a901382a28326ba230455125a471ee 100644 --- a/drivers/hwtracing/coresight/coresight-dummy.c +++ b/drivers/hwtracing/coresight/coresight-dummy.c @@ -176,6 +176,7 @@ static int dummy_probe(struct platform_device *pdev) desc.pdata = pdev->dev.platform_data; desc.dev = &pdev->dev;
- desc.cpu = -1; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index b952a1d47f12f44c15cc845914691305713028c4..b8df9e3c93df167dabd39d90b6f9491def0f6b90 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -768,6 +768,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_etb_groups;
- desc.cpu = -1;
coresight_clear_self_claim_tag(&desc.access); drvdata->csdev = coresight_register(&desc); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index a547a6d2e0bde84748f753e5529d316c4f5e82e2..2e9e824309bdab5c2983ed1a3e61b27c9d822278 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -891,6 +891,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_etm_groups;
- desc.cpu = drvdata->cpu; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index d565a73f0042e3e0b21fcf9cb94009cc25834d3d..add4cc9c1eca42b58d8f9d7bb3dc8d2ea1241ee2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -2260,6 +2260,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_etmv4_groups;
- desc.cpu = drvdata->cpu; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 3f56ceccd8c9f6deaaf783a06a0d19fb06eff7c4..c281c36a84015a73023ba9db2eae29ce13c6e880 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -261,6 +261,7 @@ static int funnel_probe(struct device *dev, struct resource *res) desc.ops = &funnel_cs_ops; desc.pdata = pdata; desc.dev = dev;
- desc.cpu = -1; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 07fc04f53b88fdec7ae18d3000046fe74c0cec9d..e947e8f0e8c4bfc4d85da385663ea3805a9b1df1 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -271,6 +271,7 @@ static int replicator_probe(struct device *dev, struct resource *res) desc.ops = &replicator_cs_ops; desc.pdata = dev->platform_data; desc.dev = dev;
- desc.cpu = -1;
drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index aca6cec7885a551ad5960986b1cdf08baa6b5a94..d60fdec640c760c4648ece8d28636936a1b9fb9b 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -899,6 +899,7 @@ static int __stm_probe(struct device *dev, struct resource *res) desc.pdata = pdata; desc.dev = dev; desc.groups = coresight_stm_groups;
- desc.cpu = -1; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index d2a6ed8bcc74d64dccc735463f14790b4e80d101..7c7d3a3cb9f18a9dfa78eb5c19dda43fa6656271 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -162,6 +162,9 @@ static int coresight_validate_source_sysfs(struct coresight_device *csdev, return -EINVAL; }
- if (coresight_is_percpu_source(csdev) && !cpu_online(csdev->cpu))
return -ENODEV;- return 0; }
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index c89fe996af23194c7e59432ce39ce20bb65937cf..5af06ae8b44d0e53a479822f25ab58e692a9696b 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -863,6 +863,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) } dev->platform_data = pdata; desc.pdata = pdata;
- desc.cpu = -1;
coresight_clear_self_claim_tag(&desc.access); drvdata->csdev = coresight_register(&desc); diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c index 96a25877b8240f20921efa979a361eb473c05600..c8530c625c0b04699e82b602e556718717a239f7 100644 --- a/drivers/hwtracing/coresight/coresight-tnoc.c +++ b/drivers/hwtracing/coresight/coresight-tnoc.c @@ -226,6 +226,8 @@ static int _tnoc_probe(struct device *dev, struct resource *res) desc.dev = dev; desc.access = CSDEV_ACCESS_IOMEM(drvdata->base); desc.groups = coresight_tnoc_groups;
- desc.cpu = -1;
- drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) { if (drvdata->atid > 0)
diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index 89c8f71f0aff06e87971976efb23feb0057fcce6..b96dfc697b1d881b853839dce196e3024f489fc3 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -593,6 +593,7 @@ static int tpda_probe(struct amba_device *adev, const struct amba_id *id) desc.dev = &adev->dev; desc.groups = tpda_attr_grps; desc.access = CSDEV_ACCESS_IOMEM(base);
- desc.cpu = -1; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c index da77bdaad0a4519bd4e0653cc92e2a333e59e60d..b61cc260ec0b043a96772a47ca0dd536ef12e290 100644 --- a/drivers/hwtracing/coresight/coresight-tpdm.c +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -1423,6 +1423,7 @@ static int tpdm_probe(struct device *dev, struct resource *res) desc.pdata = dev->platform_data; desc.dev = dev; desc.access = CSDEV_ACCESS_IOMEM(base);
- desc.cpu = -1; if (res) desc.groups = tpdm_attr_grps; drvdata->csdev = coresight_register(&desc);
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index b8560b140e0fa4bf8bf1e5c60fe67077b11798fd..a5254f36820b9e75047241d8690ff39b0f62b143 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -169,6 +169,7 @@ static int __tpiu_probe(struct device *dev, struct resource *res) desc.ops = &tpiu_cs_ops; desc.pdata = pdata; desc.dev = dev;
- desc.cpu = -1; drvdata->csdev = coresight_register(&desc);
if (!IS_ERR(drvdata->csdev)) diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index 1511f8eb95afb5b4610b8fbdacc8b174b6b08530..fc6e3551b7002634bcf6a154080a142dfe32c890 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -1289,6 +1289,7 @@ static void arm_trbe_register_coresight_cpu(struct trbe_drvdata *drvdata, int cp desc.ops = &arm_trbe_cs_ops; desc.groups = arm_trbe_groups; desc.dev = dev;
- desc.cpu = cpu; trbe_csdev = coresight_register(&desc); if (IS_ERR(trbe_csdev)) goto cpu_clear;
diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 5776f63468fa05df255d3dd716f621c37424c048..5217a9930e2632a40ba0523438052872cea62aea 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -482,6 +482,7 @@ static int smb_register_sink(struct platform_device *pdev, return -ENOMEM; } desc.access = CSDEV_ACCESS_IOMEM(drvdata->base);
- desc.cpu = -1;
drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 2131febebee93d609df1aea8534a10898b600be2..6810acc8533e0eb0c5d41a7fbe0bbd03eb94057d 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -153,6 +153,7 @@ struct csdev_access {
in the component's sysfs sub-directory.- @name: name for the coresight device, also shown under sysfs.
- @access: Describe access to the device
*/ struct coresight_desc { enum coresight_dev_type type;
- @cpu: The CPU this component is affined to (-1 for not CPU bound).
@@ -163,6 +164,7 @@ struct coresight_desc { const struct attribute_group **groups; const char *name; struct csdev_access access;
- int cpu; };
/** @@ -261,6 +263,7 @@ struct coresight_trace_id_map {
CS_MODE_SYSFS. Otherwise it must be accessed from inside thespinlock.- @orphan: true if the component has connections that haven't been linked.
- @cpu: The CPU this component is affined to (-1 for not CPU bound).
- @sysfs_sink_activated: 'true' when a sink has been selected for use via sysfs
by writing a 1 to the 'enable_sink' file. A sink can beactivated but not yet enabled. Enabling for a _sink_ happens@@ -287,6 +290,7 @@ struct coresight_device { atomic_t mode; int refcnt; bool orphan;
- int cpu; /* sink specific fields */ bool sysfs_sink_activated; struct dev_ext_attribute *ea;
On Fri, Mar 13, 2026 at 11:03:02AM +0000, Suzuki K Poulose wrote:
On 05/03/2026 10:17, Leo Yan wrote:
The current SysFS flow first enables the links and sink, then rolls back to disable them if the source fails to enable. This failure can occur if the associated CPU is offline, which causes the SMP call to fail.
Populate CPU ID into the coresight_device structure, for components that are not CPU bound, set this field to -1.
Validate whether the associated CPU is online for a per-CPU tracer. If the CPU is offline, return -ENODEV and bail out.
Reviewed-by: Yeoreum Yun yeoreum.yun@arm.com Reviewed-by: Mike Leach mike.leach@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com
While this works, how about we make it an explicit buy in from the driver for CPU bound ?
e.g., add a flag in the coresight_desc.flags = CORESIGHT_DESC_CPU_BOUND
and then desc.cpu describes the associated CPU. Otherwise, defaults to -1. That way you could restrict the changes to only those that need the CPU (ETMx and CTIs ?) Also makes any new driver safe.
Instead of adding a flag, I'd use a helper to check existed flags:
static inline bool coresight_is_cpu_bound(struct coresight_device *csdev) { if (!csdev) return false;
if (coresight_is_percpu_source(csdev)) return true;
if (csdev->type == CORESIGHT_DEV_TYPE_HELPER && csdev->subtype.helper_subtype == CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI) return true;
return false; }
Only CPU bound device will assign CPU ID, otherwise will set -1.
Thanks, Leo
On 16/03/2026 14:02, Leo Yan wrote:
On Fri, Mar 13, 2026 at 11:03:02AM +0000, Suzuki K Poulose wrote:
On 05/03/2026 10:17, Leo Yan wrote:
The current SysFS flow first enables the links and sink, then rolls back to disable them if the source fails to enable. This failure can occur if the associated CPU is offline, which causes the SMP call to fail.
Populate CPU ID into the coresight_device structure, for components that are not CPU bound, set this field to -1.
Validate whether the associated CPU is online for a per-CPU tracer. If the CPU is offline, return -ENODEV and bail out.
Reviewed-by: Yeoreum Yun yeoreum.yun@arm.com Reviewed-by: Mike Leach mike.leach@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com
While this works, how about we make it an explicit buy in from the driver for CPU bound ?
e.g., add a flag in the coresight_desc.flags = CORESIGHT_DESC_CPU_BOUND
and then desc.cpu describes the associated CPU. Otherwise, defaults to -1. That way you could restrict the changes to only those that need the CPU (ETMx and CTIs ?) Also makes any new driver safe.
Instead of adding a flag, I'd use a helper to check existed flags:
static inline bool coresight_is_cpu_bound(struct coresight_device *csdev) { if (!csdev) return false;
if (coresight_is_percpu_source(csdev)) return true; if (csdev->type == CORESIGHT_DEV_TYPE_HELPER && csdev->subtype.helper_subtype == CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI)
That works, but if a new device type comes in, we may have to expand the list. Having a flag makes much more sense and the core driver only does what has been asked for. I prefer the flags.
Cheers Suzuki
return true; return false;}
Only CPU bound device will assign CPU ID, otherwise will set -1.
Thanks, Leo
On Mon, Mar 16, 2026 at 02:28:35PM +0000, Suzuki K Poulose wrote:
[...]
e.g., add a flag in the coresight_desc.flags = CORESIGHT_DESC_CPU_BOUND
and then desc.cpu describes the associated CPU. Otherwise, defaults to -1. That way you could restrict the changes to only those that need the CPU (ETMx and CTIs ?) Also makes any new driver safe.
Instead of adding a flag, I'd use a helper to check existed flags:
static inline bool coresight_is_cpu_bound(struct coresight_device *csdev) { if (!csdev) return false;
if (coresight_is_percpu_source(csdev)) return true; if (csdev->type == CORESIGHT_DEV_TYPE_HELPER && csdev->subtype.helper_subtype == CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI)That works, but if a new device type comes in, we may have to expand the list. Having a flag makes much more sense and the core driver only does what has been asked for. I prefer the flags.
Sure, I will add a new flag. Thanks!
Add coresight_{set|clear}_percpu_source() helpers to set and clear the per-CPU source device. These helpers are called during device registration and unregistration.
If the helper is called from a CPU hotplug notifier (see etm4_probe_cpu()), it sets the pointer directly since it is already running on the local CPU.
Otherwise, to avoid races with CPU hotplug and CPU idle, it acquires the CPU lock and issues an SMP call to update the per-CPU pointer on the target CPU. If that fails, it sets the pointer as a last resort.
Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index f191a0fe802ac6ae1342872e01fff463775c8a20..cda1e9b065a056e57030e52e07502554c71014ca 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -34,6 +34,7 @@ */ DEFINE_MUTEX(coresight_mutex); static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); +static DEFINE_PER_CPU(struct coresight_device *, csdev_source);
/** * struct coresight_node - elements of a path, from source to sink @@ -82,6 +83,56 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
+static void coresight_set_percpu_source_local(void *csdev) +{ + this_cpu_write(csdev_source, csdev); +} + +static void _coresight_set_percpu_source(int cpu, struct coresight_device *csdev) +{ + /* + * Directly set per CPU pointer if running on the local CPU. This + * avoids to acquire CPU lock duplicately if it is called from CPU + * hotplug notifier, see etm4_probe_cpu(). + */ + if (get_cpu() == cpu) { + this_cpu_write(csdev_source, csdev); + put_cpu(); + return; + } + + put_cpu(); + + /* Avoid race condition with CPU hotplug */ + guard(cpus_read_lock)(); + + if (!smp_call_function_single(cpu, coresight_set_percpu_source_local, + csdev, 1)) + return; + + /* + * If SMP call fails (e.g., the CPU is offline), directly update the + * per CPU pointer as last resort. + */ + per_cpu(csdev_source, cpu) = csdev; +} + +static void coresight_set_percpu_source(struct coresight_device *csdev) +{ + if (!coresight_is_percpu_source(csdev)) + return; + + _coresight_set_percpu_source(csdev->cpu, csdev); +} + +static void coresight_clear_percpu_source(struct coresight_device *csdev) +{ + if (!coresight_is_percpu_source(csdev)) + return; + + _coresight_set_percpu_source(csdev->cpu, NULL); +} + static struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; @@ -1401,6 +1452,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
mutex_unlock(&coresight_mutex);
+ coresight_set_percpu_source(csdev); + if (cti_assoc_ops && cti_assoc_ops->add) cti_assoc_ops->add(csdev);
@@ -1427,6 +1480,7 @@ void coresight_unregister(struct coresight_device *csdev) if (cti_assoc_ops && cti_assoc_ops->remove) cti_assoc_ops->remove(csdev);
+ coresight_clear_percpu_source(csdev); mutex_lock(&coresight_mutex); etm_perf_del_symlink_sink(csdev); coresight_remove_conns(csdev);
On 05/03/2026 10:17, Leo Yan wrote:
Add coresight_{set|clear}_percpu_source() helpers to set and clear the per-CPU source device. These helpers are called during device registration and unregistration.
If the helper is called from a CPU hotplug notifier (see etm4_probe_cpu()), it sets the pointer directly since it is already running on the local CPU.
Otherwise, to avoid races with CPU hotplug and CPU idle, it acquires the CPU lock and issues an SMP call to update the per-CPU pointer on the target CPU. If that fails, it sets the pointer as a last resort.
Signed-off-by: Leo Yan leo.yan@arm.com
drivers/hwtracing/coresight/coresight-core.c | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index f191a0fe802ac6ae1342872e01fff463775c8a20..cda1e9b065a056e57030e52e07502554c71014ca 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -34,6 +34,7 @@ */ DEFINE_MUTEX(coresight_mutex); static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); +static DEFINE_PER_CPU(struct coresight_device *, csdev_source); /**
- struct coresight_node - elements of a path, from source to sink
@@ -82,6 +83,56 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); +static void coresight_set_percpu_source_local(void *csdev) +{
- this_cpu_write(csdev_source, csdev);
+}
+static void _coresight_set_percpu_source(int cpu, struct coresight_device *csdev) +{
- /*
* Directly set per CPU pointer if running on the local CPU. This* avoids to acquire CPU lock duplicately if it is called from CPU* hotplug notifier, see etm4_probe_cpu().*/- if (get_cpu() == cpu) {
this_cpu_write(csdev_source, csdev);put_cpu();return;- }
- put_cpu();
- /* Avoid race condition with CPU hotplug */
- guard(cpus_read_lock)();
- if (!smp_call_function_single(cpu, coresight_set_percpu_source_local,
csdev, 1))return;- /*
* If SMP call fails (e.g., the CPU is offline), directly update the* per CPU pointer as last resort.*/- per_cpu(csdev_source, cpu) = csdev;
+}
+static void coresight_set_percpu_source(struct coresight_device *csdev) +{
- if (!coresight_is_percpu_source(csdev))
return;- _coresight_set_percpu_source(csdev->cpu, csdev);
+}
+static void coresight_clear_percpu_source(struct coresight_device *csdev) +{
- if (!coresight_is_percpu_source(csdev))
return;- _coresight_set_percpu_source(csdev->cpu, NULL);
+}
---8>---
static struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; @@ -1401,6 +1452,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) mutex_unlock(&coresight_mutex);
- coresight_set_percpu_source(csdev);
- if (cti_assoc_ops && cti_assoc_ops->add) cti_assoc_ops->add(csdev);
@@ -1427,6 +1480,7 @@ void coresight_unregister(struct coresight_device *csdev) if (cti_assoc_ops && cti_assoc_ops->remove) cti_assoc_ops->remove(csdev);
- coresight_clear_percpu_source(csdev);
Should these be done with the mutex lock held ?
mutex_lock(&coresight_mutex); etm_perf_del_symlink_sink(csdev); coresight_remove_conns(csdev);
On Fri, Mar 13, 2026 at 11:18:20AM +0000, Suzuki K Poulose wrote:
[...]
static struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; @@ -1401,6 +1452,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) mutex_unlock(&coresight_mutex);
- coresight_set_percpu_source(csdev);
- if (cti_assoc_ops && cti_assoc_ops->add) cti_assoc_ops->add(csdev);
@@ -1427,6 +1480,7 @@ void coresight_unregister(struct coresight_device *csdev) if (cti_assoc_ops && cti_assoc_ops->remove) cti_assoc_ops->remove(csdev);
- coresight_clear_percpu_source(csdev);
Should these be done with the mutex lock held ?
If so, we will create a locking chain:
coresight_mutex -> cpus_read_lock()
Afterwards in patch 18, it uses cpus_read_lock() to protect sysfs knobs, a reversed locking chain will be established:
cpus_read_lock() -> coresight_mutex
LOCKDEP will complain for possible deadlock. This is why this patch avoids to acquire mutex when set / clear per CPU sources.
mutex_lock(&coresight_mutex); etm_perf_del_symlink_sink(csdev); coresight_remove_conns(csdev);
On 16/03/2026 14:38, Leo Yan wrote:
On Fri, Mar 13, 2026 at 11:18:20AM +0000, Suzuki K Poulose wrote:
[...]
static struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; @@ -1401,6 +1452,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) mutex_unlock(&coresight_mutex);
- coresight_set_percpu_source(csdev);
- if (cti_assoc_ops && cti_assoc_ops->add) cti_assoc_ops->add(csdev);
@@ -1427,6 +1480,7 @@ void coresight_unregister(struct coresight_device *csdev) if (cti_assoc_ops && cti_assoc_ops->remove) cti_assoc_ops->remove(csdev);
- coresight_clear_percpu_source(csdev);
Should these be done with the mutex lock held ?
If so, we will create a locking chain:
coresight_mutex -> cpus_read_lock()
Afterwards in patch 18, it uses cpus_read_lock() to protect sysfs knobs, a reversed locking chain will be established:
cpus_read_lock() -> coresight_mutex
LOCKDEP will complain for possible deadlock. This is why this patch avoids to acquire mutex when set / clear per CPU sources.
The question is, what prevents two different CPUs trying to modify the "per_cpu_source" data structure when the CPU is not online.
Suzuki
mutex_lock(&coresight_mutex); etm_perf_del_symlink_sink(csdev); coresight_remove_conns(csdev);
On Mon, Mar 16, 2026 at 05:49:47PM +0000, Suzuki K Poulose wrote:
On 16/03/2026 14:38, Leo Yan wrote:
On Fri, Mar 13, 2026 at 11:18:20AM +0000, Suzuki K Poulose wrote:
[...]
static struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; @@ -1401,6 +1452,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) mutex_unlock(&coresight_mutex);
- coresight_set_percpu_source(csdev);
- if (cti_assoc_ops && cti_assoc_ops->add) cti_assoc_ops->add(csdev);
@@ -1427,6 +1480,7 @@ void coresight_unregister(struct coresight_device *csdev) if (cti_assoc_ops && cti_assoc_ops->remove) cti_assoc_ops->remove(csdev);
- coresight_clear_percpu_source(csdev);
Should these be done with the mutex lock held ?
If so, we will create a locking chain:
coresight_mutex -> cpus_read_lock()
Afterwards in patch 18, it uses cpus_read_lock() to protect sysfs knobs, a reversed locking chain will be established:
cpus_read_lock() -> coresight_mutex
LOCKDEP will complain for possible deadlock. This is why this patch avoids to acquire mutex when set / clear per CPU sources.
The question is, what prevents two different CPUs trying to modify the "per_cpu_source" data structure when the CPU is not online.
I am struggling to establish a flow for this scenario.
Each CPU has a unique per-CPU source, and the pointer is only modified during device probe or remove. Both paths are protected by the device lock (I confirmed that __device_attach() and __device_release_driver() are protected by device_lock(dev)). Therefore concurrent updates to the same source pointer should not occur.
One possible corner case is the ETMv4 delayed probe (etm4_probe_cpu()). Since the probe is deferred and coresight_register() is invoked when a CPU is hotplugged, it could race with ETMv4 module unloading, i.e. between etm4_probe_cpu() and etm4_remove_dev().
I discussed with James for another option before: we always access per-cpu source pointer _locally_. For example, we could set the pointer in etm4_init_arch_data() and clear it in clear_etmdrvdata(). These paths are protected by the CPU lock and in SMP call, thus coresight_set_percpu_source() does not need acquire any lock.
The downside is that we set the per-cpu source pointer in ETM driver but cannot use coresight_register/coresight_unregister as common place to manage the pointer.
Thanks, Leo
On 05/03/2026 10:17, Leo Yan wrote:
Add coresight_{set|clear}_percpu_source() helpers to set and clear the per-CPU source device. These helpers are called during device registration and unregistration.
If the helper is called from a CPU hotplug notifier (see etm4_probe_cpu()), it sets the pointer directly since it is already running on the local CPU.
Otherwise, to avoid races with CPU hotplug and CPU idle, it acquires the CPU lock and issues an SMP call to update the per-CPU pointer on the target CPU. If that fails, it sets the pointer as a last resort.
Signed-off-by: Leo Yan leo.yan@arm.com
drivers/hwtracing/coresight/coresight-core.c | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index f191a0fe802ac6ae1342872e01fff463775c8a20..cda1e9b065a056e57030e52e07502554c71014ca 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -34,6 +34,7 @@ */ DEFINE_MUTEX(coresight_mutex); static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); +static DEFINE_PER_CPU(struct coresight_device *, csdev_source); /**
- struct coresight_node - elements of a path, from source to sink
@@ -82,6 +83,56 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); +static void coresight_set_percpu_source_local(void *csdev) +{
- this_cpu_write(csdev_source, csdev);
+}
+static void _coresight_set_percpu_source(int cpu, struct coresight_device *csdev) +{
While at it, why not add a coresight_get_percpu_source() and hide the implementation for the subsequent patches ?
Cheers Suzuki
- /*
* Directly set per CPU pointer if running on the local CPU. This* avoids to acquire CPU lock duplicately if it is called from CPU* hotplug notifier, see etm4_probe_cpu().*/- if (get_cpu() == cpu) {
this_cpu_write(csdev_source, csdev);put_cpu();return;- }
- put_cpu();
- /* Avoid race condition with CPU hotplug */
- guard(cpus_read_lock)();
- if (!smp_call_function_single(cpu, coresight_set_percpu_source_local,
csdev, 1))return;- /*
* If SMP call fails (e.g., the CPU is offline), directly update the* per CPU pointer as last resort.*/- per_cpu(csdev_source, cpu) = csdev;
+}
+static void coresight_set_percpu_source(struct coresight_device *csdev) +{
- if (!coresight_is_percpu_source(csdev))
return;- _coresight_set_percpu_source(csdev->cpu, csdev);
+}
+static void coresight_clear_percpu_source(struct coresight_device *csdev) +{
- if (!coresight_is_percpu_source(csdev))
return;- _coresight_set_percpu_source(csdev->cpu, NULL);
+}
- static struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev;
@@ -1401,6 +1452,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) mutex_unlock(&coresight_mutex);
- coresight_set_percpu_source(csdev);
- if (cti_assoc_ops && cti_assoc_ops->add) cti_assoc_ops->add(csdev);
@@ -1427,6 +1480,7 @@ void coresight_unregister(struct coresight_device *csdev) if (cti_assoc_ops && cti_assoc_ops->remove) cti_assoc_ops->remove(csdev);
- coresight_clear_percpu_source(csdev); mutex_lock(&coresight_mutex); etm_perf_del_symlink_sink(csdev); coresight_remove_conns(csdev);
On Fri, Mar 13, 2026 at 11:22:32AM +0000, Suzuki K Poulose wrote:
[...]
@@ -82,6 +83,56 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); +static void coresight_set_percpu_source_local(void *csdev) +{
- this_cpu_write(csdev_source, csdev);
+}
+static void _coresight_set_percpu_source(int cpu, struct coresight_device *csdev) +{
While at it, why not add a coresight_get_percpu_source() and hide the implementation for the subsequent patches ?
Good point! Will do.
The current implementation only saves and restores the context for ETM sources while ignoring the context of links. However, if funnels or replicators on a linked path resides in a CPU or cluster power domain, the hardware context for the link will be lost after resuming from low power states.
To support context management for links during CPU low power modes, a better way is to implement CPU PM callbacks in the Arm CoreSight core layer. As the core layer has sufficient information for linked paths, from tracers to links, which can be used for power management.
As a first step, this patch registers CPU PM notifier in the core layer. If a source device provides callbacks for saving and restoring context, these callbacks will be invoked in CPU suspend and resume.
Further changes will extend for controlling path.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 70 ++++++++++++++++++++++++++++ include/linux/coresight.h | 2 + 2 files changed, 72 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index cda1e9b065a056e57030e52e07502554c71014ca..5f7930ef537e2877649c404858d9fe6f593af037 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -6,6 +6,7 @@ #include <linux/acpi.h> #include <linux/bitfield.h> #include <linux/build_bug.h> +#include <linux/cpu_pm.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> @@ -1670,6 +1671,68 @@ static void coresight_release_device_list(void) } }
+static bool coresight_pm_is_needed(struct coresight_device *csdev) +{ + if (!csdev) + return false; + + /* pm_save_disable() and pm_restore_enable() must be paired */ + if (!coresight_ops(csdev)->pm_save_disable || + !coresight_ops(csdev)->pm_restore_enable) + return false; + + return true; +} + +static int coresight_pm_save(struct coresight_device *csdev) +{ + return coresight_ops(csdev)->pm_save_disable(csdev); +} + +static void coresight_pm_restore(struct coresight_device *csdev) +{ + coresight_ops(csdev)->pm_restore_enable(csdev); +} + +static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, + void *v) +{ + unsigned int cpu = smp_processor_id(); + struct coresight_device *source = per_cpu(csdev_source, cpu); + + if (!coresight_pm_is_needed(source)) + return NOTIFY_OK; + + switch (cmd) { + case CPU_PM_ENTER: + if (coresight_pm_save(source)) + return NOTIFY_BAD; + break; + case CPU_PM_EXIT: + case CPU_PM_ENTER_FAILED: + coresight_pm_restore(source); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static struct notifier_block coresight_cpu_pm_nb = { + .notifier_call = coresight_cpu_pm_notify, +}; + +static int __init coresight_pm_setup(void) +{ + return cpu_pm_register_notifier(&coresight_cpu_pm_nb); +} + +static void coresight_pm_cleanup(void) +{ + cpu_pm_unregister_notifier(&coresight_cpu_pm_nb); +} + const struct bus_type coresight_bustype = { .name = "coresight", }; @@ -1724,9 +1787,15 @@ static int __init coresight_init(void)
/* initialise the coresight syscfg API */ ret = cscfg_init(); + if (ret) + goto exit_notifier; + + ret = coresight_pm_setup(); if (!ret) return 0;
+ cscfg_exit(); +exit_notifier: atomic_notifier_chain_unregister(&panic_notifier_list, &coresight_notifier); exit_perf: @@ -1738,6 +1807,7 @@ static int __init coresight_init(void)
static void __exit coresight_exit(void) { + coresight_pm_cleanup(); cscfg_exit(); atomic_notifier_chain_unregister(&panic_notifier_list, &coresight_notifier); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 6810acc8533e0eb0c5d41a7fbe0bbd03eb94057d..63d77a064d9431d512b6407946c403f5c331d086 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -437,6 +437,8 @@ struct coresight_ops_panic { struct coresight_ops { int (*trace_id)(struct coresight_device *csdev, enum cs_mode mode, struct coresight_device *sink); + int (*pm_save_disable)(struct coresight_device *csdev); + void (*pm_restore_enable)(struct coresight_device *csdev); const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops;
Since the CoreSight core layer has registered CPU PM notifiers, this patch hooks CPU save and restore callbacks to be invoked from the core layer.
The CPU PM notifier in the ETMv4 driver is no longer needed, remove it along with its registration and unregistration code.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 59 ++++------------------ 1 file changed, 11 insertions(+), 48 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index add4cc9c1eca42b58d8f9d7bb3dc8d2ea1241ee2..ab2031647616111d0689442149bcfdb0accbb218 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1208,11 +1208,6 @@ static const struct coresight_ops_source etm4_source_ops = { .pause_perf = etm4_pause_perf, };
-static const struct coresight_ops etm4_cs_ops = { - .trace_id = coresight_etm_get_trace_id, - .source_ops = &etm4_source_ops, -}; - static bool cpu_supports_sysreg_trace(void) { u64 dfr0 = read_sysreg_s(SYS_ID_AA64DFR0_EL1); @@ -2008,8 +2003,9 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) return ret; }
-static int etm4_cpu_save(struct etmv4_drvdata *drvdata) +static int etm4_cpu_save(struct coresight_device *csdev) { + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret = 0;
if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) @@ -2125,8 +2121,10 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) etm4_cs_lock(drvdata, csa); }
-static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) +static void etm4_cpu_restore(struct coresight_device *csdev) { + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) return;
@@ -2134,38 +2132,11 @@ static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) __etm4_cpu_restore(drvdata); }
-static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, - void *v) -{ - struct etmv4_drvdata *drvdata; - unsigned int cpu = smp_processor_id(); - - if (!etmdrvdata[cpu]) - return NOTIFY_OK; - - drvdata = etmdrvdata[cpu]; - - if (WARN_ON_ONCE(drvdata->cpu != cpu)) - return NOTIFY_BAD; - - switch (cmd) { - case CPU_PM_ENTER: - if (etm4_cpu_save(drvdata)) - return NOTIFY_BAD; - break; - case CPU_PM_EXIT: - case CPU_PM_ENTER_FAILED: - etm4_cpu_restore(drvdata); - break; - default: - return NOTIFY_DONE; - } - - return NOTIFY_OK; -} - -static struct notifier_block etm4_cpu_pm_nb = { - .notifier_call = etm4_cpu_pm_notify, +static const struct coresight_ops etm4_cs_ops = { + .trace_id = coresight_etm_get_trace_id, + .pm_save_disable = etm4_cpu_save, + .pm_restore_enable = etm4_cpu_restore, + .source_ops = &etm4_source_ops, };
/* Setup PM. Deals with error conditions and counts */ @@ -2173,16 +2144,12 @@ static int __init etm4_pm_setup(void) { int ret;
- ret = cpu_pm_register_notifier(&etm4_cpu_pm_nb); - if (ret) - return ret; - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING, "arm/coresight4:starting", etm4_starting_cpu, etm4_dying_cpu);
if (ret) - goto unregister_notifier; + return ret;
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "arm/coresight4:online", @@ -2196,15 +2163,11 @@ static int __init etm4_pm_setup(void)
/* failed dyn state - remove others */ cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); - -unregister_notifier: - cpu_pm_unregister_notifier(&etm4_cpu_pm_nb); return ret; }
static void etm4_pm_clear(void) { - cpu_pm_unregister_notifier(&etm4_cpu_pm_nb); cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); if (hp_online) { cpuhp_remove_state_nocalls(hp_online);
Add a callback in the source device that returns a boolean indicating whether power management operations are required. The save and restore flow is skipped if the callback returns false.
The ETMv4 driver implements its own version's callback.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 9 ++++++++- drivers/hwtracing/coresight/coresight-etm4x-core.c | 16 ++++++++++++++++ include/linux/coresight.h | 1 + 3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 5f7930ef537e2877649c404858d9fe6f593af037..6956429ca507d850c8b8c62c65de4b6fe1a1740c 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1681,7 +1681,14 @@ static bool coresight_pm_is_needed(struct coresight_device *csdev) !coresight_ops(csdev)->pm_restore_enable) return false;
- return true; + /* + * PM callbacks are provided but pm_is_neended() is absent, it means + * no extra check is needed. + */ + if (!coresight_ops(csdev)->pm_is_needed) + return true; + + return coresight_ops(csdev)->pm_is_needed(csdev); }
static int coresight_pm_save(struct coresight_device *csdev) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index ab2031647616111d0689442149bcfdb0accbb218..d005983ee1e595f4ddc0b1f87596b1fbe4a0e41a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1861,6 +1861,21 @@ static int etm4_dying_cpu(unsigned int cpu) return 0; }
+static bool etm4_need_save_restore_context(struct coresight_device *csdev) +{ + if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) + return false; + + /* + * Save and restore the ETM Trace registers only if + * the ETM is active. + */ + if (coresight_get_mode(csdev)) + return true; + + return false; +} + static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) { int i, ret = 0; @@ -2136,6 +2151,7 @@ static const struct coresight_ops etm4_cs_ops = { .trace_id = coresight_etm_get_trace_id, .pm_save_disable = etm4_cpu_save, .pm_restore_enable = etm4_cpu_restore, + .pm_is_needed = etm4_need_save_restore_context, .source_ops = &etm4_source_ops, };
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 63d77a064d9431d512b6407946c403f5c331d086..2de2ef94d2e046effb264cc97e5cd1a61e609249 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -439,6 +439,7 @@ struct coresight_ops { struct coresight_device *sink); int (*pm_save_disable)(struct coresight_device *csdev); void (*pm_restore_enable)(struct coresight_device *csdev); + bool (*pm_is_needed)(struct coresight_device *csdev); const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops;
On 05/03/2026 10:17, Leo Yan wrote:
Add a callback in the source device that returns a boolean indicating whether power management operations are required. The save and restore flow is skipped if the callback returns false.
The ETMv4 driver implements its own version's callback.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com
drivers/hwtracing/coresight/coresight-core.c | 9 ++++++++- drivers/hwtracing/coresight/coresight-etm4x-core.c | 16 ++++++++++++++++ include/linux/coresight.h | 1 + 3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 5f7930ef537e2877649c404858d9fe6f593af037..6956429ca507d850c8b8c62c65de4b6fe1a1740c 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1681,7 +1681,14 @@ static bool coresight_pm_is_needed(struct coresight_device *csdev) !coresight_ops(csdev)->pm_restore_enable) return false;
- return true;
- /*
* PM callbacks are provided but pm_is_neended() is absent, it means* no extra check is needed.*/- if (!coresight_ops(csdev)->pm_is_needed)
return true;
Do we really need this call back ? The ETM can skip registering the PM ops when it is not needed (make them NULL) ? And the oresight_get_mode() is any way available for all devices and use that simply in the core function ?
Suzuki
- return coresight_ops(csdev)->pm_is_needed(csdev); }
static int coresight_pm_save(struct coresight_device *csdev) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index ab2031647616111d0689442149bcfdb0accbb218..d005983ee1e595f4ddc0b1f87596b1fbe4a0e41a 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1861,6 +1861,21 @@ static int etm4_dying_cpu(unsigned int cpu) return 0; } +static bool etm4_need_save_restore_context(struct coresight_device *csdev) +{
- if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED)
return false;- /*
* Save and restore the ETM Trace registers only if* the ETM is active.*/- if (coresight_get_mode(csdev))
return true;- return false;
+}
- static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) { int i, ret = 0;
@@ -2136,6 +2151,7 @@ static const struct coresight_ops etm4_cs_ops = { .trace_id = coresight_etm_get_trace_id, .pm_save_disable = etm4_cpu_save, .pm_restore_enable = etm4_cpu_restore,
- .pm_is_needed = etm4_need_save_restore_context, .source_ops = &etm4_source_ops, };
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 63d77a064d9431d512b6407946c403f5c331d086..2de2ef94d2e046effb264cc97e5cd1a61e609249 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -439,6 +439,7 @@ struct coresight_ops { struct coresight_device *sink); int (*pm_save_disable)(struct coresight_device *csdev); void (*pm_restore_enable)(struct coresight_device *csdev);
- bool (*pm_is_needed)(struct coresight_device *csdev); const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops;
On Fri, Mar 13, 2026 at 11:26:59AM +0000, Suzuki K Poulose wrote:
On 05/03/2026 10:17, Leo Yan wrote:
Add a callback in the source device that returns a boolean indicating whether power management operations are required. The save and restore flow is skipped if the callback returns false.
The ETMv4 driver implements its own version's callback.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com
drivers/hwtracing/coresight/coresight-core.c | 9 ++++++++- drivers/hwtracing/coresight/coresight-etm4x-core.c | 16 ++++++++++++++++ include/linux/coresight.h | 1 + 3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 5f7930ef537e2877649c404858d9fe6f593af037..6956429ca507d850c8b8c62c65de4b6fe1a1740c 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1681,7 +1681,14 @@ static bool coresight_pm_is_needed(struct coresight_device *csdev) !coresight_ops(csdev)->pm_restore_enable) return false;
- return true;
- /*
* PM callbacks are provided but pm_is_neended() is absent, it means* no extra check is needed.*/- if (!coresight_ops(csdev)->pm_is_needed)
return true;Do we really need this call back ? The ETM can skip registering the PM ops when it is not needed (make them NULL) ? And the oresight_get_mode() is any way available for all devices and use that simply in the core function ?
Good point! I will drop pm_is_needed() callback in new version.
Since a dedicated callback is now used to determine whether context save and restore operations are needed, performing the same check within the save and restore callbacks is redundant.
The save and restore flows currently use two-level functions: the first level handles the condition check, while the second level performs the low level operations. As the checks are no longer necessary, simplify the logic into single-level functions.
Reviewed-by: Mike Leach mike.leach@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 35 +++------------------- 1 file changed, 4 insertions(+), 31 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index d005983ee1e595f4ddc0b1f87596b1fbe4a0e41a..7f5c3ef6b9247d5500ca0dca9c16b5fe80eb8498 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1876,11 +1876,11 @@ static bool etm4_need_save_restore_context(struct coresight_device *csdev) return false; }
-static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) +static int etm4_cpu_save(struct coresight_device *csdev) { int i, ret = 0; struct etmv4_save_state *state; - struct coresight_device *csdev = drvdata->csdev; + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct csdev_access *csa; struct device *etm_dev;
@@ -2018,26 +2018,10 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) return ret; }
-static int etm4_cpu_save(struct coresight_device *csdev) -{ - struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - int ret = 0; - - if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) - return 0; - - /* - * Save and restore the ETM Trace registers only if - * the ETM is active. - */ - if (coresight_get_mode(drvdata->csdev)) - ret = __etm4_cpu_save(drvdata); - return ret; -} - -static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) +static void etm4_cpu_restore(struct coresight_device *csdev) { int i; + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etmv4_save_state *state = drvdata->save_state; struct csdev_access *csa = &drvdata->csdev->access;
@@ -2136,17 +2120,6 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) etm4_cs_lock(drvdata, csa); }
-static void etm4_cpu_restore(struct coresight_device *csdev) -{ - struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) - return; - - if (coresight_get_mode(drvdata->csdev)) - __etm4_cpu_restore(drvdata); -} - static const struct coresight_ops etm4_cs_ops = { .trace_id = coresight_etm_get_trace_id, .pm_save_disable = etm4_cpu_save,
To support dynamically enabling and disabling the CoreSight path during CPU idle, cscfg_config_sysfs_get_active_cfg() may be called by the ETM driver in an atomic context. However, the function currently acquires a mutex, which is a sleepable lock, and this is not allowed in atomic context.
To make cscfg_config_sysfs_get_active_cfg() called in the idle flow, replace the mutex with a raw spinlock to protect the active variables.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-syscfg.c | 22 +++++++++++++--------- drivers/hwtracing/coresight/coresight-syscfg.h | 2 ++ 2 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index d7f5037953d6ba7fb7f83a8012a1abc5ffd0a147..8d3992753f69cc8108725b566e0ec8380c78625a 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -957,15 +957,19 @@ int cscfg_config_sysfs_activate(struct cscfg_config_desc *config_desc, bool acti
cfg_hash = (unsigned long)config_desc->event_ea->var;
+ raw_spin_lock(&cscfg_mgr->spinlock); + if (activate) { + /* cannot be a current active value to activate this */ if (cscfg_mgr->sysfs_active_config) { err = -EBUSY; - goto exit_unlock; + } else { + err = _cscfg_activate_config(cfg_hash); + if (!err) + cscfg_mgr->sysfs_active_config = cfg_hash; } - err = _cscfg_activate_config(cfg_hash); - if (!err) - cscfg_mgr->sysfs_active_config = cfg_hash; + } else { /* disable if matching current value */ if (cscfg_mgr->sysfs_active_config == cfg_hash) { @@ -975,7 +979,8 @@ int cscfg_config_sysfs_activate(struct cscfg_config_desc *config_desc, bool acti err = -EINVAL; }
-exit_unlock: + raw_spin_unlock(&cscfg_mgr->spinlock); + mutex_unlock(&cscfg_mutex); return err; } @@ -983,9 +988,8 @@ int cscfg_config_sysfs_activate(struct cscfg_config_desc *config_desc, bool acti /* set the sysfs preset value */ void cscfg_config_sysfs_set_preset(int preset) { - mutex_lock(&cscfg_mutex); + guard(raw_spinlock)(&cscfg_mgr->spinlock); cscfg_mgr->sysfs_active_preset = preset; - mutex_unlock(&cscfg_mutex); }
/* @@ -994,10 +998,9 @@ void cscfg_config_sysfs_set_preset(int preset) */ void cscfg_config_sysfs_get_active_cfg(unsigned long *cfg_hash, int *preset) { - mutex_lock(&cscfg_mutex); + guard(raw_spinlock)(&cscfg_mgr->spinlock); *preset = cscfg_mgr->sysfs_active_preset; *cfg_hash = cscfg_mgr->sysfs_active_config; - mutex_unlock(&cscfg_mutex); } EXPORT_SYMBOL_GPL(cscfg_config_sysfs_get_active_cfg);
@@ -1201,6 +1204,7 @@ static int cscfg_create_device(void) INIT_LIST_HEAD(&cscfg_mgr->load_order_list); atomic_set(&cscfg_mgr->sys_active_cnt, 0); cscfg_mgr->load_state = CSCFG_NONE; + raw_spin_lock_init(&cscfg_mgr->spinlock);
/* setup the device */ dev = cscfg_device(); diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 66e2db890d8203853a0c3c907b48aa66dd8014e6..f52aba4608dadc1c7666e0e28c9b43dc1c33d1eb 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -42,6 +42,7 @@ enum cscfg_load_ops { * @sysfs_active_config:Active config hash used if CoreSight controlled from sysfs. * @sysfs_active_preset:Active preset index used if CoreSight controlled from sysfs. * @load_state: A multi-stage load/unload operation is in progress. + * @spinlock: Exclusive access sysfs_active_* variables. */ struct cscfg_manager { struct device dev; @@ -54,6 +55,7 @@ struct cscfg_manager { u32 sysfs_active_config; int sysfs_active_preset; enum cscfg_load_ops load_state; + raw_spinlock_t spinlock; };
/* get reference to dev in cscfg_manager */
Introduce the coresight_enable_source() helper for enabling source device, refine the comment for the imbalance between enable and disable pair functions.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 15 ++++++++++++--- drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +- drivers/hwtracing/coresight/coresight-priv.h | 3 +++ drivers/hwtracing/coresight/coresight-sysfs.c | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6956429ca507d850c8b8c62c65de4b6fe1a1740c..8e22f0993631fa3749437175ecc984ccb1cffc13 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -437,15 +437,24 @@ static void coresight_disable_helpers(struct coresight_device *csdev, }
/* - * Helper function to call source_ops(csdev)->disable and also disable the - * helpers. + * coresight_enable_source() only enables the source but does nothing for the + * associated helpers. In contrast, coresight_disable_source() calls + * source_ops(csdev)->disable() and also disables the helpers. * * There is an imbalance between coresight_enable_path() and * coresight_disable_path(). Enabling also enables the source's helpers as part * of the path, but disabling always skips the first item in the path (which is * the source), so sources and their helpers don't get disabled as part of that - * function and we need the extra step here. + * function and we need the extra step in coresight_disable_source(). */ +int coresight_enable_source(struct coresight_device *csdev, + struct perf_event *event, enum cs_mode mode, + struct coresight_path *path) +{ + return source_ops(csdev)->enable(csdev, event, mode, path); +} +EXPORT_SYMBOL_GPL(coresight_enable_source); + void coresight_disable_source(struct coresight_device *csdev, void *data) { source_ops(csdev)->disable(csdev, data); diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index f85dedf89a3f9e85d568ca4c320fa6fa6d9059ff..11c529fd4f3df973e5be9de2a7686163c1abceca 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -533,7 +533,7 @@ static void etm_event_start(struct perf_event *event, int flags) goto fail_end_stop;
/* Finally enable the tracer */ - if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF, path)) + if (coresight_enable_source(csdev, event, CS_MODE_PERF, path)) goto fail_disable_path;
/* diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 1ea882dffd703b2873e41b4ce0c2564d2ce9bbad..22fb6c5bffbf714bdbe2f4dcbfe66e453c2d1f4f 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -248,6 +248,9 @@ void coresight_add_helper(struct coresight_device *csdev,
void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev); struct coresight_device *coresight_get_percpu_sink(int cpu); +int coresight_enable_source(struct coresight_device *csdev, + struct perf_event *event, enum cs_mode mode, + struct coresight_path *path); void coresight_disable_source(struct coresight_device *csdev, void *data); void coresight_pause_source(struct coresight_device *csdev); int coresight_resume_source(struct coresight_device *csdev); diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 7c7d3a3cb9f18a9dfa78eb5c19dda43fa6656271..cb1d4f90a9c245b2c2f6e818c0f4d905b86477db 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -66,7 +66,7 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev, */ lockdep_assert_held(&coresight_mutex); if (coresight_get_mode(csdev) != CS_MODE_SYSFS) { - ret = source_ops(csdev)->enable(csdev, NULL, mode, path); + ret = coresight_enable_source(csdev, NULL, mode, path); if (ret) return ret; }
On 05/03/2026 10:17, Leo Yan wrote:
Introduce the coresight_enable_source() helper for enabling source device, refine the comment for the imbalance between enable and disable pair functions.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com
drivers/hwtracing/coresight/coresight-core.c | 15 ++++++++++++--- drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +- drivers/hwtracing/coresight/coresight-priv.h | 3 +++ drivers/hwtracing/coresight/coresight-sysfs.c | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6956429ca507d850c8b8c62c65de4b6fe1a1740c..8e22f0993631fa3749437175ecc984ccb1cffc13 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -437,15 +437,24 @@ static void coresight_disable_helpers(struct coresight_device *csdev, } /*
- Helper function to call source_ops(csdev)->disable and also disable the
- helpers.
- coresight_enable_source() only enables the source but does nothing for the
- associated helpers. In contrast, coresight_disable_source() calls
- source_ops(csdev)->disable() and also disables the helpers.
- There is an imbalance between coresight_enable_path() and
- coresight_disable_path(). Enabling also enables the source's helpers as part
- of the path, but disabling always skips the first item in the path (which is
- the source), so sources and their helpers don't get disabled as part of that
- function and we need the extra step here.
*/
- function and we need the extra step in coresight_disable_source().
+int coresight_enable_source(struct coresight_device *csdev,
struct perf_event *event, enum cs_mode mode,struct coresight_path *path)+{
- return source_ops(csdev)->enable(csdev, event, mode, path);
Please could we double check to see if the csdev is source and then make the call to future proof this ?
Rest looks fine to me.
Suzuki
+} +EXPORT_SYMBOL_GPL(coresight_enable_source);
- void coresight_disable_source(struct coresight_device *csdev, void *data) { source_ops(csdev)->disable(csdev, data);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index f85dedf89a3f9e85d568ca4c320fa6fa6d9059ff..11c529fd4f3df973e5be9de2a7686163c1abceca 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -533,7 +533,7 @@ static void etm_event_start(struct perf_event *event, int flags) goto fail_end_stop; /* Finally enable the tracer */
- if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF, path))
- if (coresight_enable_source(csdev, event, CS_MODE_PERF, path)) goto fail_disable_path;
/* diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 1ea882dffd703b2873e41b4ce0c2564d2ce9bbad..22fb6c5bffbf714bdbe2f4dcbfe66e453c2d1f4f 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -248,6 +248,9 @@ void coresight_add_helper(struct coresight_device *csdev, void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev); struct coresight_device *coresight_get_percpu_sink(int cpu); +int coresight_enable_source(struct coresight_device *csdev,
struct perf_event *event, enum cs_mode mode, void coresight_disable_source(struct coresight_device *csdev, void *data); void coresight_pause_source(struct coresight_device *csdev); int coresight_resume_source(struct coresight_device *csdev);struct coresight_path *path);diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 7c7d3a3cb9f18a9dfa78eb5c19dda43fa6656271..cb1d4f90a9c245b2c2f6e818c0f4d905b86477db 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -66,7 +66,7 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev, */ lockdep_assert_held(&coresight_mutex); if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {
ret = source_ops(csdev)->enable(csdev, NULL, mode, path);
if (ret) return ret; }ret = coresight_enable_source(csdev, NULL, mode, path);
Store the active path in the source device structure for system tracers (e.g. STM).
Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 22 ++++++++++++++++++++-- include/linux/coresight.h | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 8e22f0993631fa3749437175ecc984ccb1cffc13..80489d38f690e0139c8712161c663ee63a8a7fb3 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -451,14 +451,32 @@ int coresight_enable_source(struct coresight_device *csdev, struct perf_event *event, enum cs_mode mode, struct coresight_path *path) { - return source_ops(csdev)->enable(csdev, event, mode, path); + int ret; + + + ret = source_ops(csdev)->enable(csdev, event, mode, path); + if (ret) + return ret; + + /* + * The per-CPU source has updated its path pointer in the enable() + * callback, ensuring synchronization on the target CPU. Set the + * path pointer here for non per-CPU sources. + */ + if (!coresight_is_percpu_source(csdev)) + csdev->path = path; + + return 0; } EXPORT_SYMBOL_GPL(coresight_enable_source);
void coresight_disable_source(struct coresight_device *csdev, void *data) { source_ops(csdev)->disable(csdev, data); - coresight_disable_helpers(csdev, NULL); + coresight_disable_helpers(csdev, csdev->path); + + if (!coresight_is_percpu_source(csdev)) + csdev->path = NULL; } EXPORT_SYMBOL_GPL(coresight_disable_source);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 2de2ef94d2e046effb264cc97e5cd1a61e609249..96a1af3425a5d588fccb185a428cef5c88febdbd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -264,6 +264,7 @@ struct coresight_trace_id_map { * spinlock. * @orphan: true if the component has connections that haven't been linked. * @cpu: The CPU this component is affined to (-1 for not CPU bound). + * @path: Activated path pointer (only used for source device). * @sysfs_sink_activated: 'true' when a sink has been selected for use via sysfs * by writing a 1 to the 'enable_sink' file. A sink can be * activated but not yet enabled. Enabling for a _sink_ happens @@ -291,6 +292,7 @@ struct coresight_device { int refcnt; bool orphan; int cpu; + struct coresight_path *path; /* sink specific fields */ bool sysfs_sink_activated; struct dev_ext_attribute *ea;
Set and clear the path pointer on the target CPU during ETM enable and disable operations to avoid race conditions when retrieving the pointer in CPU PM callbacks.
Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 7f5c3ef6b9247d5500ca0dca9c16b5fe80eb8498..22812e9bc8366e9d32d0b0ca1a347b3764748985 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -241,6 +241,7 @@ void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
struct etm4_enable_arg { struct etmv4_drvdata *drvdata; + struct coresight_path *path; int rc; };
@@ -628,8 +629,12 @@ static void etm4_enable_sysfs_smp_call(void *info) arg->rc = etm4_enable_hw(arg->drvdata);
/* The tracer didn't start */ - if (arg->rc) + if (arg->rc) { coresight_set_mode(csdev, CS_MODE_DISABLED); + return; + } + + csdev->path = arg->path; }
/* @@ -897,9 +902,13 @@ static int etm4_enable_perf(struct coresight_device *csdev,
out: /* Failed to start tracer; roll back to DISABLED mode */ - if (ret) + if (ret) { coresight_set_mode(csdev, CS_MODE_DISABLED); - return ret; + return ret; + } + + csdev->path = path; + return 0; }
static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path) @@ -929,6 +938,7 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa * ensures that register writes occur when cpu is powered. */ arg.drvdata = drvdata; + arg.path = path; ret = smp_call_function_single(drvdata->cpu, etm4_enable_sysfs_smp_call, &arg, 1); if (!ret) @@ -1070,6 +1080,7 @@ static void etm4_disable_sysfs_smp_call(void *info)
etm4_disable_hw(drvdata);
+ drvdata->csdev->path = NULL; coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED); }
@@ -1099,6 +1110,7 @@ static int etm4_disable_perf(struct coresight_device *csdev, /* TRCVICTLR::SSSTATUS, bit[9] */ filters->ssstatus = (control & BIT(9));
+ drvdata->csdev->path = NULL; coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
/*
Set and clear the path pointer on the target CPU during ETM enable and disable operations.
Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-etm3x-core.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index 2e9e824309bdab5c2983ed1a3e61b27c9d822278..ebbebdf65b774893038e640ac5a75f5195834743 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -441,6 +441,7 @@ static int etm_enable_hw(struct etm_drvdata *drvdata)
struct etm_enable_arg { struct etm_drvdata *drvdata; + struct coresight_path *path; int rc; };
@@ -462,8 +463,12 @@ static void etm_enable_sysfs_smp_call(void *info) arg->rc = etm_enable_hw(arg->drvdata);
/* The tracer didn't start */ - if (arg->rc) + if (arg->rc) { coresight_set_mode(csdev, CS_MODE_DISABLED); + return; + } + + csdev->path = arg->path; }
static int etm_cpu_id(struct coresight_device *csdev) @@ -499,10 +504,13 @@ static int etm_enable_perf(struct coresight_device *csdev, ret = etm_enable_hw(drvdata);
/* Failed to start tracer; roll back to DISABLED mode */ - if (ret) + if (ret) { coresight_set_mode(csdev, CS_MODE_DISABLED); + return ret; + }
- return ret; + csdev->path = path; + return 0; }
static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path) @@ -590,6 +598,7 @@ static void etm_disable_sysfs_smp_call(void *info)
etm_disable_hw(drvdata);
+ drvdata->csdev->path = NULL; coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED); }
@@ -614,6 +623,7 @@ static void etm_disable_perf(struct coresight_device *csdev)
CS_LOCK(drvdata->csa.base);
+ drvdata->csdev->path = NULL; coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
/*
Since the path pointer is stored in the source's structure, retrieve it directly when disabling the path.
As a result, the global variables used for caching path pointers are no longer needed. Remove them to simplify the code.
Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-sysfs.c | 79 +++------------------------ 1 file changed, 9 insertions(+), 70 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index cb1d4f90a9c245b2c2f6e818c0f4d905b86477db..9e94505bccf4ac91207f2f5393ae57ae077d61d6 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -12,19 +12,6 @@ #include "coresight-priv.h" #include "coresight-trace-id.h"
-/* - * Use IDR to map the hash of the source's device name - * to the pointer of path for the source. The idr is for - * the sources which aren't associated with CPU. - */ -static DEFINE_IDR(path_idr); - -/* - * When operating Coresight drivers from the sysFS interface, only a single - * path can exist from a tracer (associated to a CPU) to a sink. - */ -static DEFINE_PER_CPU(struct coresight_path *, tracer_path); - ssize_t coresight_simple_show_pair(struct device *_dev, struct device_attribute *attr, char *buf) { @@ -170,11 +157,10 @@ static int coresight_validate_source_sysfs(struct coresight_device *csdev,
int coresight_enable_sysfs(struct coresight_device *csdev) { - int cpu, ret = 0; + int ret = 0; struct coresight_device *sink; struct coresight_path *path; enum coresight_dev_subtype_source subtype; - u32 hash;
subtype = csdev->subtype.source_subtype;
@@ -226,35 +212,6 @@ int coresight_enable_sysfs(struct coresight_device *csdev) if (ret) goto err_source;
- switch (subtype) { - case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: - /* - * When working from sysFS it is important to keep track - * of the paths that were created so that they can be - * undone in 'coresight_disable()'. Since there can only - * be a single session per tracer (when working from sysFS) - * a per-cpu variable will do just fine. - */ - cpu = source_ops(csdev)->cpu_id(csdev); - per_cpu(tracer_path, cpu) = path; - break; - case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: - case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: - case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: - /* - * Use the hash of source's device name as ID - * and map the ID to the pointer of the path. - */ - hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); - ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); - if (ret) - goto err_source; - break; - default: - /* We can't be here */ - break; - } - out: mutex_unlock(&coresight_mutex); return ret; @@ -270,9 +227,8 @@ EXPORT_SYMBOL_GPL(coresight_enable_sysfs);
void coresight_disable_sysfs(struct coresight_device *csdev) { - int cpu, ret; - struct coresight_path *path = NULL; - u32 hash; + int ret; + struct coresight_path *path;
mutex_lock(&coresight_mutex);
@@ -280,32 +236,15 @@ void coresight_disable_sysfs(struct coresight_device *csdev) if (ret) goto out;
+ /* + * coresight_disable_source_sysfs() clears the 'csdev->path' pointer + * when disabling the source. Retrieve the path pointer here. + */ + path = csdev->path; + if (!coresight_disable_source_sysfs(csdev, NULL)) goto out;
- switch (csdev->subtype.source_subtype) { - case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: - cpu = source_ops(csdev)->cpu_id(csdev); - path = per_cpu(tracer_path, cpu); - per_cpu(tracer_path, cpu) = NULL; - break; - case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: - case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM: - case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: - hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); - /* Find the path by the hash. */ - path = idr_find(&path_idr, hash); - if (path == NULL) { - pr_err("Path is not found for %s\n", dev_name(&csdev->dev)); - goto out; - } - idr_remove(&path_idr, hash); - break; - default: - /* We can't be here */ - break; - } - coresight_disable_path(path); coresight_release_path(path);
Introduce an 'in_idle' argument to the path. When set to true for idle flow, it skips to touch the sink device to avoid long latency caused by the sink operations.
This is a preparation for managing the path during CPU idle.
Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 17 +++++++++++++---- include/linux/coresight.h | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 80489d38f690e0139c8712161c663ee63a8a7fb3..420e5a9cf38e64f133aa6780987d2d7c2804feb4 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -531,6 +531,10 @@ static void coresight_disable_path_from(struct coresight_path *path, CORESIGHT_DEV_TYPE_SINK : CORESIGHT_DEV_TYPE_LINK;
+ /* To reduce latency, CPU idle does not touch the sink */ + if (path->in_idle && type == CORESIGHT_DEV_TYPE_SINK) + continue; + switch (type) { case CORESIGHT_DEV_TYPE_SINK: coresight_disable_sink(csdev); @@ -597,10 +601,6 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) csdev = nd->csdev; type = csdev->type;
- /* Enable all helpers adjacent to the path first */ - ret = coresight_enable_helpers(csdev, mode, path); - if (ret) - goto err_disable_path; /* * ETF devices are tricky... They can be a link or a sink, * depending on how they are configured. If an ETF has been @@ -612,6 +612,15 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) CORESIGHT_DEV_TYPE_SINK : CORESIGHT_DEV_TYPE_LINK;
+ /* To reduce latency, CPU idle does not touch the sink */ + if (path->in_idle && type == CORESIGHT_DEV_TYPE_SINK) + continue; + + /* Enable all helpers adjacent to the path first */ + ret = coresight_enable_helpers(csdev, mode, path); + if (ret) + goto err_disable_path; + switch (type) { case CORESIGHT_DEV_TYPE_SINK: ret = coresight_enable_sink(csdev, mode, path); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 96a1af3425a5d588fccb185a428cef5c88febdbd..5f778cd41e6bbb58393278f72feeaedabb0094d2 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -332,11 +332,13 @@ struct coresight_dev_list { * @path_list: path from source to sink. * @trace_id: trace_id of the whole path. * @handle: handle of the aux_event. + * @in_idle: A flag to indicate if it is in CPU idle. */ struct coresight_path { struct list_head path_list; u8 trace_id; struct perf_output_handle *handle; + bool in_idle; };
enum cs_mode {
On 05/03/2026 10:17, Leo Yan wrote:
Introduce an 'in_idle' argument to the path. When set to true for idle flow, it skips to touch the sink device to avoid long latency caused by the sink operations.
This is a preparation for managing the path during CPU idle.
Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com
Does this need to be saved in the path ? Could we solve this by doing something like :
coresight_disable_path_till(coresight_path, list_head* end)
Disable path till "end" node.
coresight_disable_path(path) { coresight_disable_path_till(path, coresight_get_sink(path); }
Suzuki
drivers/hwtracing/coresight/coresight-core.c | 17 +++++++++++++---- include/linux/coresight.h | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 80489d38f690e0139c8712161c663ee63a8a7fb3..420e5a9cf38e64f133aa6780987d2d7c2804feb4 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -531,6 +531,10 @@ static void coresight_disable_path_from(struct coresight_path *path, CORESIGHT_DEV_TYPE_SINK : CORESIGHT_DEV_TYPE_LINK;
/* To reduce latency, CPU idle does not touch the sink */if (path->in_idle && type == CORESIGHT_DEV_TYPE_SINK)continue;- switch (type) { case CORESIGHT_DEV_TYPE_SINK: coresight_disable_sink(csdev);
@@ -597,10 +601,6 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) csdev = nd->csdev; type = csdev->type;
/* Enable all helpers adjacent to the path first */ret = coresight_enable_helpers(csdev, mode, path);if (ret) /*goto err_disable_path;
- ETF devices are tricky... They can be a link or a sink,
- depending on how they are configured. If an ETF has been
@@ -612,6 +612,15 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode) CORESIGHT_DEV_TYPE_SINK : CORESIGHT_DEV_TYPE_LINK;
/* To reduce latency, CPU idle does not touch the sink */if (path->in_idle && type == CORESIGHT_DEV_TYPE_SINK)continue;/* Enable all helpers adjacent to the path first */ret = coresight_enable_helpers(csdev, mode, path);if (ret)goto err_disable_path;- switch (type) { case CORESIGHT_DEV_TYPE_SINK: ret = coresight_enable_sink(csdev, mode, path);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 96a1af3425a5d588fccb185a428cef5c88febdbd..5f778cd41e6bbb58393278f72feeaedabb0094d2 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -332,11 +332,13 @@ struct coresight_dev_list {
- @path_list: path from source to sink.
- @trace_id: trace_id of the whole path.
- @handle: handle of the aux_event.
*/ struct coresight_path { struct list_head path_list; u8 trace_id; struct perf_output_handle *handle;
- @in_idle: A flag to indicate if it is in CPU idle.
- bool in_idle; };
enum cs_mode {
Control links and helpers on an activated path during CPU idle. Set the "path->in_idle" flag to true during idle to guide path control.
Since coresight_disable_path() doesn't handle a source device's helpers, explicitly disable them alongside the source device.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 420e5a9cf38e64f133aa6780987d2d7c2804feb4..8763ca1d81674efa3dea9479a6a6c4025b652559 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1729,12 +1729,33 @@ static bool coresight_pm_is_needed(struct coresight_device *csdev)
static int coresight_pm_save(struct coresight_device *csdev) { - return coresight_ops(csdev)->pm_save_disable(csdev); + int ret; + + if (WARN_ON(!csdev->path)) + return -EINVAL; + + /* in_idle flag will be used for path controlling */ + csdev->path->in_idle = true; + + ret = coresight_ops(csdev)->pm_save_disable(csdev); + if (ret) { + csdev->path->in_idle = false; + return ret; + } + + coresight_disable_helpers(csdev, csdev->path); + coresight_disable_path(csdev->path); + return 0; }
static void coresight_pm_restore(struct coresight_device *csdev) { + if (WARN_ON(!csdev->path)) + return; + + coresight_enable_path(csdev->path, coresight_get_mode(csdev)); coresight_ops(csdev)->pm_restore_enable(csdev); + csdev->path->in_idle = false; }
static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
Unlike a system level's sink, the per-CPU sink may lose power during CPU idle states. Currently, this refers specifically to TRBE as the sink.
This commit registers save and restore callbacks for the sink via the PM notifier.
Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 8763ca1d81674efa3dea9479a6a6c4025b652559..25ed45129375d83adb2e1b30664aef15535e8164 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1727,6 +1727,22 @@ static bool coresight_pm_is_needed(struct coresight_device *csdev) return coresight_ops(csdev)->pm_is_needed(csdev); }
+static int coresight_pm_sink_save(struct coresight_device *sink) +{ + if (!sink || !coresight_ops(sink)->pm_save_disable) + return 0; + + return coresight_ops(sink)->pm_save_disable(sink); +} + +static void coresight_pm_sink_restore(struct coresight_device *sink) +{ + if (!sink || !coresight_ops(sink)->pm_restore_enable) + return; + + coresight_ops(sink)->pm_restore_enable(sink); +} + static int coresight_pm_save(struct coresight_device *csdev) { int ret; @@ -1745,7 +1761,18 @@ static int coresight_pm_save(struct coresight_device *csdev)
coresight_disable_helpers(csdev, csdev->path); coresight_disable_path(csdev->path); + + ret = coresight_pm_sink_save(coresight_get_sink(csdev->path)); + if (ret) + goto failed_out; + return 0; + +failed_out: + coresight_enable_path(csdev->path, coresight_get_mode(csdev)); + coresight_ops(csdev)->pm_restore_enable(csdev); + csdev->path->in_idle = false; + return ret; }
static void coresight_pm_restore(struct coresight_device *csdev) @@ -1753,6 +1780,7 @@ static void coresight_pm_restore(struct coresight_device *csdev) if (WARN_ON(!csdev->path)) return;
+ coresight_pm_sink_restore(coresight_get_sink(csdev->path)); coresight_enable_path(csdev->path, coresight_get_mode(csdev)); coresight_ops(csdev)->pm_restore_enable(csdev); csdev->path->in_idle = false;
From: Yabin Cui yabinc@google.com
Similar to ETE, TRBE may lose its context when a CPU enters low power state. To make things worse, if ETE is restored without TRBE being restored, an enabled source device with no enabled sink devices can cause CPU hang on some devices (e.g., Pixel 9).
The save and restore flows are described in the section K5.5 "Context switching" of Arm ARM (ARM DDI 0487 L.a). This commit adds save and restore callbacks with following the software usages defined in the architecture manual.
Given that some bit fields may be reset to an unknown value after a CPU power cycle, this commit always save and restore all register contexts.
Signed-off-by: Yabin Cui yabinc@google.com Co-developed-by: Leo Yan leo.yan@arm.com Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-trbe.c | 59 +++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index fc6e3551b7002634bcf6a154080a142dfe32c890..8ce8074a298024fcb1df35f0243b68c7d80a3b58 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -116,6 +116,20 @@ static int trbe_errata_cpucaps[] = { */ #define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256
+/* + * struct trbe_save_state: Register values representing TRBE state + * @trblimitr - Trace Buffer Limit Address Register value + * @trbbaser - Trace Buffer Base Register value + * @trbptr - Trace Buffer Write Pointer Register value + * @trbsr - Trace Buffer Status Register value + */ +struct trbe_save_state { + u64 trblimitr; + u64 trbbaser; + u64 trbptr; + u64 trbsr; +}; + /* * struct trbe_cpudata: TRBE instance specific data * @trbe_flag - TRBE dirty/access flag support @@ -134,6 +148,7 @@ struct trbe_cpudata { enum cs_mode mode; struct trbe_buf *buf; struct trbe_drvdata *drvdata; + struct trbe_save_state save_state; DECLARE_BITMAP(errata, TRBE_ERRATA_MAX); };
@@ -1189,6 +1204,46 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev) return IRQ_HANDLED; }
+static int arm_trbe_save(struct coresight_device *csdev) +{ + struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev); + struct trbe_save_state *state = &cpudata->save_state; + + state->trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1); + + /* Disable the unit, ensure the writes to memory are complete */ + if (state->trblimitr & TRBLIMITR_EL1_E) + trbe_drain_and_disable_local(cpudata); + + state->trbbaser = read_sysreg_s(SYS_TRBBASER_EL1); + state->trbptr = read_sysreg_s(SYS_TRBPTR_EL1); + state->trbsr = read_sysreg_s(SYS_TRBSR_EL1); + return 0; +} + +static void arm_trbe_restore(struct coresight_device *csdev) +{ + struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev); + struct trbe_save_state *state = &cpudata->save_state; + + write_sysreg_s(state->trbbaser, SYS_TRBBASER_EL1); + write_sysreg_s(state->trbptr, SYS_TRBPTR_EL1); + write_sysreg_s(state->trbsr, SYS_TRBSR_EL1); + + if (!(state->trblimitr & TRBLIMITR_EL1_E)) { + write_sysreg_s(state->trblimitr, SYS_TRBLIMITR_EL1); + } else { + /* + * The section K5.5 Context switching, Arm ARM (ARM DDI 0487 + * L.a), S_PKLXF requires a Context synchronization event to + * guarantee the Trace Buffer Unit will observe the new values + * of the system registers. + */ + isb(); + set_trbe_enabled(cpudata, state->trblimitr); + } +} + static const struct coresight_ops_sink arm_trbe_sink_ops = { .enable = arm_trbe_enable, .disable = arm_trbe_disable, @@ -1198,7 +1253,9 @@ static const struct coresight_ops_sink arm_trbe_sink_ops = { };
static const struct coresight_ops arm_trbe_cs_ops = { - .sink_ops = &arm_trbe_sink_ops, + .pm_save_disable = arm_trbe_save, + .pm_restore_enable = arm_trbe_restore, + .sink_ops = &arm_trbe_sink_ops, };
static ssize_t align_show(struct device *dev, struct device_attribute *attr, char *buf)
Except for system tracers (e.g. STM), other sources treat multiple enables as equivalent to a single enable. The device mode already tracks the binary state, so it is redundant to operate refcount.
Refactor to maintain the refcount only for system sources. This simplifies future CPU PM handling without refcount logic.
Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-sysfs.c | 35 +++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 9e94505bccf4ac91207f2f5393ae57ae077d61d6..04c1425eb37d69c6feb8e49446fc58ee57a5e82e 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -58,7 +58,17 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev, return ret; }
- csdev->refcnt++; + /* + * There could be multiple applications driving the software + * source. So keep the refcount for each such user when the + * source is already enabled. + * + * No need to increment the reference counter for other source + * types, as multiple enables are the same as a single enable. + */ + if (csdev->subtype.source_subtype == + CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) + csdev->refcnt++;
return 0; } @@ -80,7 +90,10 @@ static bool coresight_disable_source_sysfs(struct coresight_device *csdev, if (coresight_get_mode(csdev) != CS_MODE_SYSFS) return false;
- csdev->refcnt--; + if (csdev->subtype.source_subtype == + CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) + csdev->refcnt--; + if (csdev->refcnt == 0) { coresight_disable_source(csdev, data); return true; @@ -159,10 +172,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev) { int ret = 0; struct coresight_device *sink; - struct coresight_path *path; - enum coresight_dev_subtype_source subtype; - - subtype = csdev->subtype.source_subtype; + struct coresight_path *path = NULL;
mutex_lock(&coresight_mutex);
@@ -176,16 +186,8 @@ int coresight_enable_sysfs(struct coresight_device *csdev) * coresight_enable_source() so can still race with Perf mode which * doesn't hold coresight_mutex. */ - if (coresight_get_mode(csdev) == CS_MODE_SYSFS) { - /* - * There could be multiple applications driving the software - * source. So keep the refcount for each such user when the - * source is already enabled. - */ - if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) - csdev->refcnt++; - goto out; - } + if (coresight_get_mode(csdev) == CS_MODE_SYSFS) + goto enable_source;
sink = coresight_find_activated_sysfs_sink(csdev); if (!sink) { @@ -208,6 +210,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev) if (ret) goto err_path;
+enable_source: ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, path); if (ret) goto err_source;
The hotplug lock is acquired and released in etm{3|4}_disable_sysfs(), which are low-level functions. This prevents us from a new solution for hotplug.
Firstly, hotplug callbacks cannot invoke etm{3|4}_disable_sysfs() to disable the source; otherwise, a deadlock issue occurs. The reason is that, in the hotplug flow, the kernel acquires the hotplug lock before calling callbacks. Subsequently, if coresight_disable_source() is invoked and it calls etm{3|4}_disable_sysfs(), the hotplug lock will be acquired twice, leading to a double lock issue.
Secondly, when hotplugging a CPU on or off, if we want to manipulate all components on a path attached to the CPU, we need to maintain atomicity for the entire path. Otherwise, a race condition may occur with users setting the same path via the Sysfs knobs, ultimately causing mess states in CoreSight components.
This patch moves the hotplug locking from etm{3|4}_disable_sysfs() into enable_source_store(). As a result, when users control the Sysfs knobs, the whole flow is protected by hotplug locking, ensuring it is mutual exclusive with hotplug callbacks.
Note, the paired function etm{3|4}_enable_sysfs() does not use hotplug locking, which is why this patch does not modify it.
Reviewed-by: Mike Leach mike.leach@linaro.org Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-etm3x-core.c | 8 -------- drivers/hwtracing/coresight/coresight-etm4x-core.c | 9 --------- drivers/hwtracing/coresight/coresight-sysfs.c | 7 +++++++ 3 files changed, 7 insertions(+), 17 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index ebbebdf65b774893038e640ac5a75f5195834743..eec3cde45bb4ea59f58271c5e4a521da2a723ffe 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -637,13 +637,6 @@ static void etm_disable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- /* - * Taking hotplug lock here protects from clocks getting disabled - * with tracing being left on (crash scenario) if user disable occurs - * after cpu online mask indicates the cpu is offline but before the - * DYING hotplug callback is serviced by the ETM driver. - */ - cpus_read_lock(); spin_lock(&drvdata->spinlock);
/* @@ -654,7 +647,6 @@ static void etm_disable_sysfs(struct coresight_device *csdev) drvdata, 1);
spin_unlock(&drvdata->spinlock); - cpus_read_unlock();
/* * we only release trace IDs when resetting sysfs. diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 22812e9bc8366e9d32d0b0ca1a347b3764748985..b74da6f0ad7cc9e7afa3df4d1ab22d902e0a272c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1125,13 +1125,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- /* - * Taking hotplug lock here protects from clocks getting disabled - * with tracing being left on (crash scenario) if user disable occurs - * after cpu online mask indicates the cpu is offline but before the - * DYING hotplug callback is serviced by the ETM driver. - */ - cpus_read_lock(); raw_spin_lock(&drvdata->spinlock);
/* @@ -1145,8 +1138,6 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
cscfg_csdev_disable_active_config(csdev);
- cpus_read_unlock(); - /* * we only release trace IDs when resetting sysfs. * This permits sysfs users to read the trace ID after the trace diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c index 04c1425eb37d69c6feb8e49446fc58ee57a5e82e..c8d45f69072546bc5ae61e22646000cd47530dba 100644 --- a/drivers/hwtracing/coresight/coresight-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-sysfs.c @@ -305,6 +305,13 @@ static ssize_t enable_source_store(struct device *dev, if (ret) return ret;
+ /* + * CoreSight hotplug callbacks in core layer control a activated path + * from its source to sink. Taking hotplug lock here protects a race + * condition with hotplug callbacks. + */ + guard(cpus_read_lock)(); + if (val) { ret = coresight_enable_sysfs(csdev); if (ret)
This commit moves CPU hotplug callbacks from ETMv4 driver to core layer. The motivation is the core layer can control all components on an activated path rather but not only managing tracer in ETMv4 driver.
The perf event layer will disable CoreSight PMU event 'cs_etm' when hotplug off a CPU. That means a perf mode will be always converted to disabled mode in CPU hotplug. Arm CoreSight CPU hotplug callbacks only need to handle the Sysfs mode and ignore the perf mode.
The core layer disables and releases the active path when hotplug-off a CPU. When hotplug-in a CPU, it does not re-enable the path, in this case, users need to re-enable the trace via Sysfs interface.
Tested-by: James Clark james.clark@linaro.org Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 42 +++++++++++++++++++++- drivers/hwtracing/coresight/coresight-etm3x-core.c | 40 --------------------- drivers/hwtracing/coresight/coresight-etm4x-core.c | 37 ------------------- 3 files changed, 41 insertions(+), 78 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 25ed45129375d83adb2e1b30664aef15535e8164..8dd784a5b756ce0a410a1c7d83d6417809b62b4d 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1786,6 +1786,33 @@ static void coresight_pm_restore(struct coresight_device *csdev) csdev->path->in_idle = false; }
+static int coresight_dying_cpu(unsigned int cpu) +{ + struct coresight_device *source = per_cpu(csdev_source, cpu); + struct coresight_path *path; + + if (!source || !source->path) + return 0; + + /* + * The perf event layer will disable PMU events in the CPU hotplug. + * CoreSight driver should never handle the CS_MODE_PERF case. + */ + if (coresight_get_mode(source) != CS_MODE_SYSFS) + return 0; + + /* + * Save 'source->path' here, as it will be cleared in + * coresight_disable_source(). + */ + path = source->path; + + coresight_disable_source(source, NULL); + coresight_disable_path(path); + coresight_release_path(path); + return 0; +} + static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, void *v) { @@ -1817,11 +1844,24 @@ static struct notifier_block coresight_cpu_pm_nb = {
static int __init coresight_pm_setup(void) { - return cpu_pm_register_notifier(&coresight_cpu_pm_nb); + int ret; + + ret = cpu_pm_register_notifier(&coresight_cpu_pm_nb); + if (ret) + return ret; + + ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING, + "arm/coresight-core:starting", + NULL, coresight_dying_cpu); + if (ret) + cpu_pm_unregister_notifier(&coresight_cpu_pm_nb); + + return ret; }
static void coresight_pm_cleanup(void) { + cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); cpu_pm_unregister_notifier(&coresight_cpu_pm_nb); }
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index eec3cde45bb4ea59f58271c5e4a521da2a723ffe..cd6a48fa767862cc30e66a6e448c0d7f2e504fd8 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -706,35 +706,6 @@ static int etm_online_cpu(unsigned int cpu) return 0; }
-static int etm_starting_cpu(unsigned int cpu) -{ - if (!etmdrvdata[cpu]) - return 0; - - spin_lock(&etmdrvdata[cpu]->spinlock); - if (!etmdrvdata[cpu]->os_unlock) { - etm_os_unlock(etmdrvdata[cpu]); - etmdrvdata[cpu]->os_unlock = true; - } - - if (coresight_get_mode(etmdrvdata[cpu]->csdev)) - etm_enable_hw(etmdrvdata[cpu]); - spin_unlock(&etmdrvdata[cpu]->spinlock); - return 0; -} - -static int etm_dying_cpu(unsigned int cpu) -{ - if (!etmdrvdata[cpu]) - return 0; - - spin_lock(&etmdrvdata[cpu]->spinlock); - if (coresight_get_mode(etmdrvdata[cpu]->csdev)) - etm_disable_hw(etmdrvdata[cpu]); - spin_unlock(&etmdrvdata[cpu]->spinlock); - return 0; -} - static bool etm_arch_supported(u8 arch) { switch (arch) { @@ -802,13 +773,6 @@ static int __init etm_hp_setup(void) { int ret;
- ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING, - "arm/coresight:starting", - etm_starting_cpu, etm_dying_cpu); - - if (ret) - return ret; - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "arm/coresight:online", etm_online_cpu, NULL); @@ -819,15 +783,11 @@ static int __init etm_hp_setup(void) return 0; }
- /* failed dyn state - remove others */ - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); - return ret; }
static void etm_hp_clear(void) { - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); if (hp_online) { cpuhp_remove_state_nocalls(hp_online); hp_online = 0; diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index b74da6f0ad7cc9e7afa3df4d1ab22d902e0a272c..cd7c2f80f103a1b748b471a873a3eb0e67b833dd 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -1837,33 +1837,6 @@ static int etm4_online_cpu(unsigned int cpu) return 0; }
-static int etm4_starting_cpu(unsigned int cpu) -{ - if (!etmdrvdata[cpu]) - return 0; - - raw_spin_lock(&etmdrvdata[cpu]->spinlock); - if (!etmdrvdata[cpu]->os_unlock) - etm4_os_unlock(etmdrvdata[cpu]); - - if (coresight_get_mode(etmdrvdata[cpu]->csdev)) - etm4_enable_hw(etmdrvdata[cpu]); - raw_spin_unlock(&etmdrvdata[cpu]->spinlock); - return 0; -} - -static int etm4_dying_cpu(unsigned int cpu) -{ - if (!etmdrvdata[cpu]) - return 0; - - raw_spin_lock(&etmdrvdata[cpu]->spinlock); - if (coresight_get_mode(etmdrvdata[cpu]->csdev)) - etm4_disable_hw(etmdrvdata[cpu]); - raw_spin_unlock(&etmdrvdata[cpu]->spinlock); - return 0; -} - static bool etm4_need_save_restore_context(struct coresight_device *csdev) { if (pm_save_enable != PARAM_PM_SAVE_SELF_HOSTED) @@ -2136,13 +2109,6 @@ static int __init etm4_pm_setup(void) { int ret;
- ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING, - "arm/coresight4:starting", - etm4_starting_cpu, etm4_dying_cpu); - - if (ret) - return ret; - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "arm/coresight4:online", etm4_online_cpu, NULL); @@ -2153,14 +2119,11 @@ static int __init etm4_pm_setup(void) return 0; }
- /* failed dyn state - remove others */ - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); return ret; }
static void etm4_pm_clear(void) { - cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); if (hp_online) { cpuhp_remove_state_nocalls(hp_online); hp_online = 0;
On 05/03/2026 10:17 am, Leo Yan wrote:
This series focuses on CoreSight path power management. The changes can be divided into four parts for review:
Patches 01~06: Refactor the CPU idle flow with moving common code into the CoreSight core layer. Patches 07~14: Add link control during CPU idle. Patches 15~16: Support the sink (TRBE) control during CPU idle. Patches 17~19: Move the CPU hotplug flow into the coresight core layer and simplify the code.
This series is rebased on the coresight-next branch and has been verified on Juno-r2 and FVP RevC.
Changes in v6:
- Rebase on the latest coresight-next branch.
- Always save and restore TRBE context during idle (Will).
- Use get_cpu() / put_cpu() when set the per CPU source pointer.
- Link to v5: https://lore.kernel.org/r/20251119-arm_coresight_path_power_management_impro...
Changes in v5:
- Set the per-CPU source pointer on target CPU (Suzuki).
- Reused existed enable/disable buffer functions in TRBE callbacks (James).
- Refactored refcount for source devices in SysFS mode.
- Released path in cpu-hotplug off flow to avoid memory leak.
- Updated ETMv3 driver when move common code into core layer.
- Rebased on the latest coresight-next branch.
- Link to v4: https://lore.kernel.org/r/20251104-arm_coresight_path_power_management_impro...
Changes in v4:
- Changed to store path pointer in coresight_device, this is easier for fetching path pointer based on source device (Mike).
- Dropped changes in CTI driver.
- Only disabled path for CPU hot-plugged off but not enable path for hot-plugged in.
- Removed James' test tags for modified patches.
- Link to v3: https://lore.kernel.org/r/20250915-arm_coresight_power_management_fix-v3-0-e...
Signed-off-by: Leo Yan leo.yan@arm.com
For the unreviewed commits:
Reviewed-by: James Clark james.clark@linaro.org
And did a bit more testing on Juno:
Tested-by: James Clark james.clark@linaro.org
Leo Yan (18): coresight: sysfs: Validate CPU online status for per-CPU sources coresight: Set per-CPU source pointer coresight: Register CPU PM notifier in core layer coresight: etm4x: Hook CPU PM callbacks coresight: Add callback to determine if PM is needed coresight: etm4x: Remove redundant condition checks in save and restore coresight: syscfg: Use spinlock to protect active variables coresight: Introduce coresight_enable_source() helper coresight: Save active path for system tracers coresight: etm4x: Set active path on target CPU coresight: etm3x: Set active path on target CPU coresight: sysfs: Use source's path pointer for path control coresight: Add 'in_idle' argument to path coresight: Control path during CPU idle coresight: Add PM callbacks for sink device coresight: sysfs: Increment refcount only for system tracers coresight: Take hotplug lock in enable_source_store() for Sysfs mode coresight: Move CPU hotplug callbacks to core layer
Yabin Cui (1): coresight: trbe: Save and restore state across CPU low power state
drivers/hwtracing/coresight/coresight-catu.c | 1 + drivers/hwtracing/coresight/coresight-core.c | 273 ++++++++++++++++++++- drivers/hwtracing/coresight/coresight-ctcu-core.c | 1 + drivers/hwtracing/coresight/coresight-cti-core.c | 1 + drivers/hwtracing/coresight/coresight-dummy.c | 1 + drivers/hwtracing/coresight/coresight-etb10.c | 1 + drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +- drivers/hwtracing/coresight/coresight-etm3x-core.c | 65 ++--- drivers/hwtracing/coresight/coresight-etm4x-core.c | 153 +++--------- drivers/hwtracing/coresight/coresight-funnel.c | 1 + drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight-replicator.c | 1 + drivers/hwtracing/coresight/coresight-stm.c | 1 + drivers/hwtracing/coresight/coresight-syscfg.c | 22 +- drivers/hwtracing/coresight/coresight-syscfg.h | 2 + drivers/hwtracing/coresight/coresight-sysfs.c | 126 +++------- drivers/hwtracing/coresight/coresight-tmc-core.c | 1 + drivers/hwtracing/coresight/coresight-tnoc.c | 2 + drivers/hwtracing/coresight/coresight-tpda.c | 1 + drivers/hwtracing/coresight/coresight-tpdm.c | 1 + drivers/hwtracing/coresight/coresight-tpiu.c | 1 + drivers/hwtracing/coresight/coresight-trbe.c | 60 ++++- drivers/hwtracing/coresight/ultrasoc-smb.c | 1 + include/linux/coresight.h | 11 + 24 files changed, 458 insertions(+), 274 deletions(-)
base-commit: 9c5ef7a30d9044f8706bd02bfdc4eff7266f3e25 change-id: 20251104-arm_coresight_path_power_management_improvement-dab4966f8280
Best regards,