diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6d65c43d574f..b7a26d67e39d 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1543,6 +1543,7 @@ coresight_init_device(struct coresight_desc *desc) csdev->ops = desc->ops; csdev->access = desc->access; csdev->orphan = true; + raw_spin_lock_init(&csdev->perf_lock); if (desc->flags & CORESIGHT_DESC_CPU_BOUND) { csdev->cpu = desc->cpu; diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 09b21a711a87..93a93b6c5d74 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -211,6 +211,49 @@ static void free_sink_buffer(struct etm_event_data *event_data) sink_ops(sink)->free_buffer(event_data->snk_config); } +static bool coresight_claim_perf_event(struct coresight_device *csdev, + struct perf_event *event) +{ + if (WARN_ON_ONCE(!csdev || !event)) + return false; + + if (coresight_is_percpu_sink(csdev)) + return true; + + guard(raw_spinlock_irqsave)(&csdev->perf_lock); + + if (!csdev->perf_event_count) { + csdev->perf_owner = event->owner; + csdev->perf_target = event->hw.target; + } else if (csdev->perf_owner != event->owner || + csdev->perf_target != event->hw.target) { + return false; + } + + csdev->perf_event_count++; + return true; +} + +static void coresight_release_perf_event(struct coresight_device *csdev) +{ + if (WARN_ON_ONCE(!csdev)) + return; + + if (coresight_is_percpu_sink(csdev)) + return; + + guard(raw_spinlock_irqsave)(&csdev->perf_lock); + + if (WARN_ON_ONCE(!csdev->perf_event_count)) + return; + + if (--csdev->perf_event_count) + return; + + csdev->perf_owner = NULL; + csdev->perf_target = NULL; +} + static void free_event_data(struct work_struct *work) { int cpu; @@ -243,6 +286,7 @@ static void free_event_data(struct work_struct *work) * coresight_trace_id_perf_stop() frees all IDs. */ coresight_trace_id_perf_stop(&sink->perf_sink_id_map); + coresight_release_perf_event(sink); coresight_release_path(*ppath); } @@ -399,24 +443,34 @@ etm_event_build_path(struct perf_event *event, int cpu, goto out; } + /* System sinks can only be shared by compatible perf events. */ + if (!coresight_claim_perf_event(sink, event)) + goto out; + /* * Building a path doesn't enable it, it simply builds a * list of devices from source to sink that can be * referenced later when the path is actually needed. */ path = coresight_build_path(source, sink); - if (IS_ERR(path)) - goto out; + if (IS_ERR(path)) { + path = NULL; + goto out_release_perf; + } /* ensure we can allocate a trace ID for this CPU */ ret = coresight_path_assign_trace_id(path, CS_MODE_PERF); if (ret) { coresight_release_path(path); path = NULL; - goto out; + goto out_release_perf; } coresight_trace_id_perf_start(&sink->perf_sink_id_map); + goto out; + +out_release_perf: + coresight_release_perf_event(sink); out: coresight_put_percpu_source_ref(source); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index ddf18c970e34..039269c48a8b 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -277,6 +277,10 @@ struct coresight_trace_id_map { * activated if it's used via Perf. * @ea: Device attribute for sink representation under PMU directory. * @def_sink: cached reference to default sink found for this device. + * @perf_lock: Protects the perf event ownership fields. + * @perf_event_count: Number of compatible perf event path claims. + * @perf_owner: Owner of the perf events using this device. + * @perf_target: Target of the perf events using this device. * @nr_links: number of sysfs links created to other components from this * device. These will appear in the "connections" group. * @has_conns_grp: Have added a "connections" group for sysfs links. @@ -302,6 +306,10 @@ struct coresight_device { struct dev_ext_attribute *ea; struct coresight_device *def_sink; struct coresight_trace_id_map perf_sink_id_map; + raw_spinlock_t perf_lock; + unsigned int perf_event_count; + struct task_struct *perf_owner; + struct task_struct *perf_target; /* sysfs links between components */ int nr_links; bool has_conns_grp;