diff options
| author | Leo Yan <leo.yan@arm.com> | 2026-05-15 23:08:30 +0300 |
|---|---|---|
| committer | Suzuki K Poulose <suzuki.poulose@arm.com> | 2026-05-18 12:18:47 +0300 |
| commit | 39c40892f1a45908fbc57c334ac503da0c80d77c (patch) | |
| tree | 1880341eb9d9500442290c7795ad7703469bf990 | |
| parent | a2e91258e8647f729eed80e13dc2423aa5fb7417 (diff) | |
| download | linux-39c40892f1a45908fbc57c334ac503da0c80d77c.tar.xz | |
coresight: Control path during CPU idle
Extend the CPU PM flow to control the path: disable from source up to
the node before the sink, then re-enable the same range on restore.
To avoid latency, control it up to the node before the sink.
Track per-CPU PM restore failures using percpu_pm_failed. Once a CPU
hits a restore failure, set the percpu_pm_failed and return NOTIFY_BAD
on subsequent notifications to avoid repeating half-completed
transitions.
Setting percpu_pm_failed permanently blocks CPU PM on that CPU. Such
failures are typically seen during development; disabling PM operations
simplifies the implementation, and a warning highlights the issue.
Clear this flag when the source device is unregistered.
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Link: https://lore.kernel.org/r/20260515-arm_coresight_path_power_management_improvement-v14-23-f88c4a3ecfe9@arm.com
| -rw-r--r-- | drivers/hwtracing/coresight/coresight-core.c | 93 |
1 files changed, 78 insertions, 15 deletions
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 7f6febd20faa..fc60b72a4abf 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -38,6 +38,7 @@ static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); static DEFINE_RAW_SPINLOCK(coresight_dev_lock); static DEFINE_PER_CPU(struct coresight_device *, csdev_source); +static DEFINE_PER_CPU(bool, percpu_pm_failed); /** * struct coresight_node - elements of a path, from source to sink @@ -121,6 +122,9 @@ static void coresight_clear_percpu_source(struct coresight_device *csdev) if (!csdev || !coresight_is_percpu_source(csdev)) return; + /* Clear percpu_pm_failed */ + per_cpu(percpu_pm_failed, csdev->cpu) = false; + guard(raw_spinlock_irqsave)(&coresight_dev_lock); /* The per-CPU pointer should contain the same csdev */ @@ -1843,7 +1847,7 @@ static void coresight_release_device_list(void) } } -static struct coresight_device *coresight_cpu_get_active_source(void) +static struct coresight_path *coresight_cpu_get_active_path(void) { struct coresight_device *source; bool is_active = false; @@ -1859,22 +1863,32 @@ static struct coresight_device *coresight_cpu_get_active_source(void) /* * It is expected to run in atomic context, so it cannot be preempted - * to disable the source. Here returns the active source pointer - * without concern that its state may change. Since the build path has - * taken a reference on the component, the source can be safely used - * by the caller. + * to disable the path. Here returns the active path pointer without + * concern that its state may change. Since the build path has taken + * a reference on the component, the path can be safely used by the + * caller. */ - return is_active ? source : NULL; + return is_active ? source->path : NULL; } -static int coresight_pm_is_needed(struct coresight_device *csdev) +/* Return: 1 if PM is required, 0 if skip, or a negative error */ +static int coresight_pm_is_needed(struct coresight_path *path) { - if (!csdev) + struct coresight_device *source; + + if (this_cpu_read(percpu_pm_failed)) + return -EIO; + + if (!path) + return 0; + + source = coresight_get_source(path); + if (!source) return 0; /* pm_save_disable() and pm_restore_enable() must be paired */ - if (coresight_ops(csdev)->pm_save_disable && - coresight_ops(csdev)->pm_restore_enable) + if (coresight_ops(source)->pm_save_disable && + coresight_ops(source)->pm_restore_enable) return 1; return 0; @@ -1890,22 +1904,71 @@ static void coresight_pm_device_restore(struct coresight_device *csdev) coresight_ops(csdev)->pm_restore_enable(csdev); } +static int coresight_pm_save(struct coresight_path *path) +{ + struct coresight_device *source = coresight_get_source(path); + struct coresight_node *from, *to; + int ret; + + ret = coresight_pm_device_save(source); + if (ret) + return ret; + + from = coresight_path_first_node(path); + /* Disable up to the node before sink */ + to = list_prev_entry(coresight_path_last_node(path), link); + coresight_disable_path_from_to(path, from, to); + + return 0; +} + +static void coresight_pm_restore(struct coresight_path *path) +{ + struct coresight_device *source = coresight_get_source(path); + struct coresight_node *from, *to; + int ret; + + from = coresight_path_first_node(path); + /* Enable up to the node before sink */ + to = list_prev_entry(coresight_path_last_node(path), link); + ret = coresight_enable_path_from_to(path, coresight_get_mode(source), + from, to); + if (ret) + goto path_failed; + + coresight_pm_device_restore(source); + return; + +path_failed: + pr_err("Failed in coresight PM restore on CPU%d: %d\n", + smp_processor_id(), ret); + + /* + * Once PM fails on a CPU, set percpu_pm_failed and leave it set until + * reboot. This prevents repeated partial transitions during idle + * entry and exit. + */ + this_cpu_write(percpu_pm_failed, true); +} + static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, void *v) { - struct coresight_device *csdev = coresight_cpu_get_active_source(); + struct coresight_path *path = coresight_cpu_get_active_path(); + int ret; - if (!coresight_pm_is_needed(csdev)) - return NOTIFY_DONE; + ret = coresight_pm_is_needed(path); + if (ret <= 0) + return ret ? NOTIFY_BAD : NOTIFY_DONE; switch (cmd) { case CPU_PM_ENTER: - if (coresight_pm_device_save(csdev)) + if (coresight_pm_save(path)) return NOTIFY_BAD; break; case CPU_PM_EXIT: case CPU_PM_ENTER_FAILED: - coresight_pm_device_restore(csdev); + coresight_pm_restore(path); break; default: return NOTIFY_DONE; |
