Control links and helpers on an active path during CPU idle.
The helper coresight_get_local_cpu_path() uses coresight_dev_lock to exclusive access to the per-CPU source pointer, then retrieves the path pointer (csdev->path). The path pointer is only set via an SMP call on the local CPU, which is serialized with CPU PM notifiers.
If the CPU PM notifier retrieves a non-NULL path pointer, it is safe to iterate over the path and control it up to the node before the sink to avoid latency.
Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 70 ++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index a74a595dd61f1a004ee0ebc9600127c928ee9f16..5060ccbc8f4c82eb14ec081ec05709391a585a11 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -131,6 +131,29 @@ struct coresight_device *coresight_get_percpu_source(int cpu) return per_cpu(csdev_source, cpu); }
+/* + * The source device's csdev->path is always set on the local CPU. + * CPU PM notifiers also run on the same CPU, so accesses to + * csdev->path are serialized. + * + * When the path is constructed, each component on the path takes module + * reference counts, and the source's csdev->path is set last. Therefore, + * when the CPU PM notifier retrieves the path on the local CPU, a non-NULL + * csdev->path guarantees that the path is safe to iterate over. + */ +static struct coresight_path *coresight_get_local_cpu_path(void) +{ + struct coresight_device *csdev; + + guard(raw_spinlock_irqsave)(&coresight_dev_lock); + + csdev = this_cpu_read(csdev_source); + if (!csdev) + return NULL; + + return csdev->path; +} + static struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; @@ -1768,8 +1791,14 @@ static void coresight_release_device_list(void) } }
-static bool coresight_pm_is_needed(struct coresight_device *csdev) +static bool coresight_pm_is_needed(struct coresight_path *path) { + struct coresight_device *csdev; + + if (!path) + return false; + + csdev = coresight_get_source(path); if (!csdev) return false;
@@ -1795,33 +1824,56 @@ static void coresight_pm_device_restore(struct coresight_device *csdev) coresight_ops(csdev)->pm_restore_enable(csdev); }
-static int coresight_pm_save(struct coresight_device *source) +static int coresight_pm_save(struct coresight_path *path) { - return coresight_pm_device_save(source); + struct coresight_device *source; + struct coresight_node *from, *to; + int ret; + + source = coresight_get_source(path); + ret = coresight_pm_device_save(source); + if (ret) + return ret; + + from = coresight_path_first_node(path); + /* Up to the node before sink to avoid latency */ + 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_device *source) +static void coresight_pm_restore(struct coresight_path *path) { + struct coresight_device *source; + struct coresight_node *from, *to; + + source = coresight_get_source(path); + from = coresight_path_first_node(path); + /* Up to the node before sink to avoid latency */ + to = list_prev_entry(coresight_path_last_node(path), link); + coresight_enable_path_from_to(path, coresight_get_mode(source), + from, to); + coresight_pm_device_restore(source); }
static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, void *v) { - struct coresight_device *source = - coresight_get_percpu_source(smp_processor_id()); + struct coresight_path *path = coresight_get_local_cpu_path();
- if (!coresight_pm_is_needed(source)) + if (!coresight_pm_is_needed(path)) return NOTIFY_OK;
switch (cmd) { case CPU_PM_ENTER: - if (coresight_pm_save(source)) + if (coresight_pm_save(path)) return NOTIFY_BAD; break; case CPU_PM_EXIT: case CPU_PM_ENTER_FAILED: - coresight_pm_restore(source); + coresight_pm_restore(path); break; default: return NOTIFY_DONE;