Comparing numeric PIDs for ownership no longer matches the sharing rules in coresight_sink_can_share(), change it to compare the session identity instead.
Now that we're not using a numeric PID for ownership we can't use the IDR map. Change it to a simple linear realloc'd array, it's unlikely the performance difference is measurable, systems wouldn't have more than a handful of shared sources for one sink, and this is only accessed once at setup.
Assisted-by: Codex:GPT-5.6-Sol Fixes: 8d03cfd16a72 ("coresight: tmc-etr: Add support for CPU-wide trace scenarios") Signed-off-by: James Clark james.clark@linaro.org --- drivers/hwtracing/coresight/coresight-etb10.c | 3 +- drivers/hwtracing/coresight/coresight-etm-perf.c | 4 +- drivers/hwtracing/coresight/coresight-tmc-core.c | 4 +- drivers/hwtracing/coresight/coresight-tmc-etf.c | 5 +- drivers/hwtracing/coresight/coresight-tmc-etr.c | 139 ++++++++++++----------- drivers/hwtracing/coresight/coresight-tmc.h | 16 ++- drivers/hwtracing/coresight/coresight-trbe.c | 3 +- drivers/hwtracing/coresight/ultrasoc-smb.c | 3 +- include/linux/coresight.h | 4 +- 9 files changed, 100 insertions(+), 81 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index a827f76b8144..6a4a1edf46ba 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -371,7 +371,8 @@ static int etb_disable(struct coresight_device *csdev) }
static void *etb_alloc_buffer(struct coresight_device *csdev, - struct perf_event *event, void **pages, + struct perf_event *event, + struct etm_session_id *owner, void **pages, int nr_pages, bool overwrite) { int node; diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 28cf319a39da..652beee2edbd 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -517,7 +517,9 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, * sinks. */ event_data->snk_config = - sink_ops(sink)->alloc_buffer(sink, event, pages, + sink_ops(sink)->alloc_buffer(sink, event, + &event_data->session_id, + pages, nr_pages, overwrite); if (!event_data->snk_config) goto err; diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index c89fe996af23..873868718b14 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -9,7 +9,6 @@ #include <linux/init.h> #include <linux/types.h> #include <linux/device.h> -#include <linux/idr.h> #include <linux/io.h> #include <linux/iommu.h> #include <linux/err.h> @@ -832,8 +831,7 @@ static int __tmc_probe(struct device *dev, struct resource *res) ret = tmc_etr_setup_caps(dev, devid, &desc.access); if (ret) goto out; - idr_init(&drvdata->idr); - mutex_init(&drvdata->idr_mutex); + mutex_init(&drvdata->perf_bufs_mutex); dev_list = "tmc_etr"; break; case TMC_CONFIG_TYPE_ETF: diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 3836063031d7..d90090b846ab 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -419,8 +419,9 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, }
static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, - struct perf_event *event, void **pages, - int nr_pages, bool overwrite) + struct perf_event *event, + struct etm_session_id *owner, + void **pages, int nr_pages, bool overwrite) { int node; struct cs_buffers *buf; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index be5ed04a554f..324d94e6d7e1 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -8,7 +8,6 @@ #include <linux/coresight.h> #include <linux/dma-mapping.h> #include <linux/iommu.h> -#include <linux/idr.h> #include <linux/mutex.h> #include <linux/refcount.h> #include <linux/slab.h> @@ -37,8 +36,6 @@ struct etr_buf_hw { * etr_perf_buffer - Perf buffer used for ETR * @drvdata - The ETR drvdaga this buffer has been allocated for. * @etr_buf - Actual buffer used by the ETR - * @pid - The PID of the session owner that etr_perf_buffer - * belongs to. * @snaphost - Perf session mode * @nr_pages - Number of pages in the ring buffer. * @pages - Array of Pages in the ring buffer. @@ -46,7 +43,6 @@ struct etr_buf_hw { struct etr_perf_buffer { struct tmc_drvdata *drvdata; struct etr_buf *etr_buf; - pid_t pid; bool snapshot; int nr_pages; void **pages; @@ -1396,16 +1392,16 @@ alloc_etr_buf(struct tmc_drvdata *drvdata, struct perf_event *event, return ERR_PTR(-ENOMEM); }
-static struct etr_buf * -get_perf_etr_buf_cpu_wide(struct tmc_drvdata *drvdata, - struct perf_event *event, int nr_pages, - void **pages, bool snapshot) +static struct etr_buf *get_perf_etr_buf_cpu_wide(struct tmc_drvdata *drvdata, + struct perf_event *event, + struct etm_session_id *owner, + int nr_pages, void **pages, + bool snapshot) { - int ret; - pid_t pid = task_pid_nr(event->owner); - struct etr_buf *etr_buf; + struct etr_buf_mapping *perf_bufs; + struct etr_buf *etr_buf, *existing; + unsigned int i;
-retry: /* * An etr_perf_buffer is associated with an event and holds a reference * to the AUX ring buffer that was created for that event. In CPU-wide @@ -1424,69 +1420,75 @@ get_perf_etr_buf_cpu_wide(struct tmc_drvdata *drvdata, * allocated for this session. If so it is shared with this event, * otherwise it is created. */ - mutex_lock(&drvdata->idr_mutex); - etr_buf = idr_find(&drvdata->idr, pid); - if (etr_buf) { - refcount_inc(&etr_buf->refcount); - mutex_unlock(&drvdata->idr_mutex); - return etr_buf; + mutex_lock(&drvdata->perf_bufs_mutex); + for (i = 0; i < drvdata->nr_perf_bufs; i++) { + if (etm_perf_compare_session(&drvdata->perf_bufs[i].owner, owner)) { + etr_buf = drvdata->perf_bufs[i].buf; + refcount_inc(&etr_buf->refcount); + mutex_unlock(&drvdata->perf_bufs_mutex); + return etr_buf; + } }
/* If we made it here no buffer has been allocated, do so now. */ - mutex_unlock(&drvdata->idr_mutex); + mutex_unlock(&drvdata->perf_bufs_mutex);
etr_buf = alloc_etr_buf(drvdata, event, nr_pages, pages, snapshot); if (IS_ERR(etr_buf)) return etr_buf;
- /* Now that we have a buffer, add it to the IDR. */ - mutex_lock(&drvdata->idr_mutex); - ret = idr_alloc(&drvdata->idr, etr_buf, pid, pid + 1, GFP_KERNEL); - mutex_unlock(&drvdata->idr_mutex); - - /* Another event with this session ID has allocated this buffer. */ - if (ret == -ENOSPC) { - tmc_free_etr_buf(etr_buf); - goto retry; + mutex_lock(&drvdata->perf_bufs_mutex); + for (i = 0; i < drvdata->nr_perf_bufs; i++) { + if (etm_perf_compare_session(&drvdata->perf_bufs[i].owner, owner)) { + existing = drvdata->perf_bufs[i].buf; + refcount_inc(&existing->refcount); + mutex_unlock(&drvdata->perf_bufs_mutex); + tmc_free_etr_buf(etr_buf); + return existing; + } }
- /* The IDR can't allocate room for a new session, abandon ship. */ - if (ret == -ENOMEM) { + perf_bufs = krealloc_array(drvdata->perf_bufs, + drvdata->nr_perf_bufs + 1, + sizeof(*perf_bufs), GFP_KERNEL); + if (!perf_bufs) { + mutex_unlock(&drvdata->perf_bufs_mutex); tmc_free_etr_buf(etr_buf); - return ERR_PTR(ret); + return ERR_PTR(-ENOMEM); }
+ drvdata->perf_bufs = perf_bufs; + perf_bufs[drvdata->nr_perf_bufs].owner = *owner; + perf_bufs[drvdata->nr_perf_bufs].buf = etr_buf; + drvdata->nr_perf_bufs++; + mutex_unlock(&drvdata->perf_bufs_mutex);
return etr_buf; }
-static struct etr_buf * -get_perf_etr_buf_per_thread(struct tmc_drvdata *drvdata, - struct perf_event *event, int nr_pages, - void **pages, bool snapshot) +static struct etr_buf *get_perf_etr_buf(struct tmc_drvdata *drvdata, + struct perf_event *event, + struct etm_session_id *owner, + int nr_pages, void **pages, + bool snapshot) { /* - * In per-thread mode the etr_buf isn't shared, so just go ahead - * with memory allocation. + * Events without a CPU must have inherit=false so always target a + * single process. etm_perf_sink_can_share() prevents other single + * process events from sharing sinks, so take a shortcut and go ahead + * with memory allocation without using shared drvdata->perf_bufs. */ - return alloc_etr_buf(drvdata, event, nr_pages, pages, snapshot); -} - -static struct etr_buf * -get_perf_etr_buf(struct tmc_drvdata *drvdata, struct perf_event *event, - int nr_pages, void **pages, bool snapshot) -{ if (event->cpu == -1) - return get_perf_etr_buf_per_thread(drvdata, event, nr_pages, - pages, snapshot); + return alloc_etr_buf(drvdata, event, nr_pages, pages, snapshot);
- return get_perf_etr_buf_cpu_wide(drvdata, event, nr_pages, + return get_perf_etr_buf_cpu_wide(drvdata, event, owner, nr_pages, pages, snapshot); }
static struct etr_perf_buffer * tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, struct perf_event *event, - int nr_pages, void **pages, bool snapshot) + struct etm_session_id *owner, int nr_pages, void **pages, + bool snapshot) { int node; struct etr_buf *etr_buf; @@ -1498,7 +1500,8 @@ tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, struct perf_event *event, if (!etr_perf) return ERR_PTR(-ENOMEM);
- etr_buf = get_perf_etr_buf(drvdata, event, nr_pages, pages, snapshot); + etr_buf = get_perf_etr_buf(drvdata, event, owner, nr_pages, pages, + snapshot); if (!IS_ERR(etr_buf)) goto done;
@@ -1508,7 +1511,7 @@ tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, struct perf_event *event, done: /* * Keep a reference to the ETR this buffer has been allocated for - * in order to have access to the IDR in tmc_free_etr_buffer(). + * in order to have access to the buffer array in tmc_free_etr_buffer(). */ etr_perf->drvdata = drvdata; etr_perf->etr_buf = etr_buf; @@ -1516,22 +1519,21 @@ tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, struct perf_event *event, return etr_perf; }
- static void *tmc_alloc_etr_buffer(struct coresight_device *csdev, - struct perf_event *event, void **pages, + struct perf_event *event, + struct etm_session_id *owner, void **pages, int nr_pages, bool snapshot) { struct etr_perf_buffer *etr_perf; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- etr_perf = tmc_etr_setup_perf_buf(drvdata, event, + etr_perf = tmc_etr_setup_perf_buf(drvdata, event, owner, nr_pages, pages, snapshot); if (IS_ERR(etr_perf)) { dev_dbg(&csdev->dev, "Unable to allocate ETR buffer\n"); return NULL; }
- etr_perf->pid = task_pid_nr(event->owner); etr_perf->snapshot = snapshot; etr_perf->nr_pages = nr_pages; etr_perf->pages = pages; @@ -1543,28 +1545,33 @@ static void tmc_free_etr_buffer(void *config) { struct etr_perf_buffer *etr_perf = config; struct tmc_drvdata *drvdata = etr_perf->drvdata; - struct etr_buf *buf, *etr_buf = etr_perf->etr_buf; + struct etr_buf *etr_buf = etr_perf->etr_buf; + unsigned int i;
if (!etr_buf) goto free_etr_perf_buffer;
- mutex_lock(&drvdata->idr_mutex); + mutex_lock(&drvdata->perf_bufs_mutex); /* If we are not the last one to use the buffer, don't touch it. */ if (!refcount_dec_and_test(&etr_buf->refcount)) { - mutex_unlock(&drvdata->idr_mutex); + mutex_unlock(&drvdata->perf_bufs_mutex); goto free_etr_perf_buffer; }
- /* We are the last one, remove from the IDR and free the buffer. */ - buf = idr_remove(&drvdata->idr, etr_perf->pid); - mutex_unlock(&drvdata->idr_mutex); + for (i = 0; i < drvdata->nr_perf_bufs; i++) { + if (drvdata->perf_bufs[i].buf != etr_buf) + continue;
- /* - * Something went very wrong if the buffer associated with this ID - * is not the same in the IDR. Leak to avoid use after free. - */ - if (buf && WARN_ON(buf != etr_buf)) - goto free_etr_perf_buffer; + drvdata->nr_perf_bufs--; + drvdata->perf_bufs[i] = + drvdata->perf_bufs[drvdata->nr_perf_bufs]; + if (!drvdata->nr_perf_bufs) { + kfree(drvdata->perf_bufs); + drvdata->perf_bufs = NULL; + } + break; + } + mutex_unlock(&drvdata->perf_bufs_mutex);
tmc_free_etr_buf(etr_perf->etr_buf);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 9b153b7910fb..0125f2ad78ed 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -8,7 +8,6 @@ #define _CORESIGHT_TMC_H
#include <linux/dma-mapping.h> -#include <linux/idr.h> #include <linux/miscdevice.h> #include <linux/mutex.h> #include <linux/refcount.h> @@ -192,6 +191,11 @@ struct etr_buf { void *private; };
+struct etr_buf_mapping { + struct etm_session_id owner; + struct etr_buf *buf; +}; + /** * @paddr : Start address of reserved memory region. * @vaddr : Corresponding CPU virtual address. @@ -239,8 +243,9 @@ struct tmc_resrv_buf { * @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the * device configuration register (DEVID) * @etr_mode: User preferred mode of the ETR device, default auto mode. - * @idr: Holds etr_bufs allocated for this ETR. - * @idr_mutex: Access serialisation for idr. + * @perf_bufs: CPU-wide Perf buffers and their owners. + * @nr_perf_bufs: Number of entries in @perf_bufs. + * @perf_bufs_mutex: Access serialisation for @perf_bufs. * @sysfs_buf: SYSFS buffer for ETR. * @perf_buf: PERF buffer for ETR. * @resrv_buf: Used by ETR as hardware trace buffer and for trace data @@ -274,8 +279,9 @@ struct tmc_drvdata { u32 trigger_cntr; u32 etr_caps; enum etr_mode etr_mode; - struct idr idr; - struct mutex idr_mutex; + struct etr_buf_mapping *perf_bufs; + unsigned int nr_perf_bufs; + struct mutex perf_bufs_mutex; struct etr_buf *sysfs_buf; struct etr_buf *perf_buf; struct tmc_resrv_buf resrv_buf; diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c index c7cbca45f2de..81841dc95e0c 100644 --- a/drivers/hwtracing/coresight/coresight-trbe.c +++ b/drivers/hwtracing/coresight/coresight-trbe.c @@ -746,7 +746,8 @@ static unsigned long trbe_get_trace_size(struct perf_output_handle *handle, }
static void *arm_trbe_alloc_buffer(struct coresight_device *csdev, - struct perf_event *event, void **pages, + struct perf_event *event, + struct etm_session_id *owner, void **pages, int nr_pages, bool snapshot) { struct trbe_buf *buf; diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c index 20a950b9dd4f..f66ec53070d7 100644 --- a/drivers/hwtracing/coresight/ultrasoc-smb.c +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -302,7 +302,8 @@ static int smb_disable(struct coresight_device *csdev) }
static void *smb_alloc_buffer(struct coresight_device *csdev, - struct perf_event *event, void **pages, + struct perf_event *event, + struct etm_session_id *owner, void **pages, int nr_pages, bool overwrite) { struct cs_buffers *buf; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 1c06a9800cfe..9e29f2cbf557 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -37,6 +37,7 @@ #define CORESIGHT_UNLOCK 0xc5acce55
extern const struct bus_type coresight_bustype; +struct etm_session_id;
enum coresight_dev_type { CORESIGHT_DEV_TYPE_SINK, @@ -371,7 +372,8 @@ struct coresight_ops_sink { struct coresight_path *path); int (*disable)(struct coresight_device *csdev); void *(*alloc_buffer)(struct coresight_device *csdev, - struct perf_event *event, void **pages, + struct perf_event *event, + struct etm_session_id *owner, void **pages, int nr_pages, bool overwrite); void (*free_buffer)(void *config); unsigned long (*update_buffer)(struct coresight_device *csdev,