#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>
struct perf_event *event, 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 *etr_buf, *curr;
-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
* 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);
+ xa_lock(&drvdata->bufs);
+ etr_buf = xa_load(&drvdata->bufs, pid);
if (etr_buf) {
refcount_inc(&etr_buf->refcount);
- mutex_unlock(&drvdata->idr_mutex);
+ xa_unlock(&drvdata->bufs);
return etr_buf;
}
/* If we made it here no buffer has been allocated, do so now. */
- mutex_unlock(&drvdata->idr_mutex);
+ xa_unlock(&drvdata->bufs);
etr_buf = alloc_etr_buf(drvdata, event, nr_pages, pages, snapshot);
if (IS_ERR(etr_buf))
refcount_set(&etr_buf->refcount, 1);
- /* 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);
+ /* Now that we have a buffer, add it to the array. */
+ curr = xa_cmpxchg(&drvdata->bufs, pid, NULL, etr_buf, GFP_KERNEL);
+ if (!curr)
+ return etr_buf;
- /* Another event with this session ID has allocated this buffer. */
- if (ret == -ENOSPC) {
- tmc_free_etr_buf(etr_buf);
- goto retry;
- }
+ tmc_free_etr_buf(etr_buf);
- /* The IDR can't allocate room for a new session, abandon ship. */
- if (ret == -ENOMEM) {
- tmc_free_etr_buf(etr_buf);
- return ERR_PTR(ret);
- }
+ /* Can't allocate room for a new session, abandon ship. */
+ if (xa_is_err(curr))
+ return ERR_PTR(xa_err(curr));
-
- return etr_buf;
+ /* Use the one in the array after all */
+ refcount_inc(&curr->refcount);
+ return curr;
}
static struct etr_buf *
if (!etr_buf)
goto free_etr_perf_buffer;
- mutex_lock(&drvdata->idr_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);
+ if (!refcount_dec_and_lock(&etr_buf->refcount, &drvdata->bufs.xa_lock))
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);
+ /* We are the last one, remove from the array and free the buffer. */
+ buf = __xa_erase(&drvdata->bufs, etr_perf->pid);
+ xa_unlock(&drvdata->bufs);
/*
* Something went very wrong if the buffer associated with this ID
- * is not the same in the IDR. Leak to avoid use after free.
+ * is not the same in the array. Leak to avoid use after free.
*/
if (buf && WARN_ON(buf != etr_buf))
goto free_etr_perf_buffer;
- tmc_free_etr_buf(etr_perf->etr_buf);
+ tmc_free_etr_buf(etr_buf);
free_etr_perf_buffer:
kfree(etr_perf);
#define _CORESIGHT_TMC_H
#include <linux/dma-mapping.h>
-#include <linux/idr.h>
+#include <linux/xarray.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/refcount.h>
* @trigger_cntr: amount of words to store after a trigger.
* @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the
* device configuration register (DEVID)
- * @idr: Holds etr_bufs allocated for this ETR.
- * @idr_mutex: Access serialisation for idr.
+ * @bufs: Holds etr_bufs allocated for this ETR.
* @perf_data: PERF buffer for ETR.
* @sysfs_data: SYSFS buffer for ETR.
*/
enum tmc_mem_intf_width memwidth;
u32 trigger_cntr;
u32 etr_caps;
- struct idr idr;
- struct mutex idr_mutex;
+ struct xarray bufs;
struct etr_buf *sysfs_buf;
void *perf_data;
};