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);