summaryrefslogtreecommitdiff
path: root/drivers/hwtracing/coresight/coresight-tmc-etr.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-tmc-etr.c')
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc-etr.c43
1 files changed, 22 insertions, 21 deletions
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index e0d83ee01b77..cee82e52c4ea 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1307,6 +1307,19 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
/*
+ * Since the sysfs buffer allocation and the hardware enablement is not
+ * in the same critical region, it's possible to race with the perf.
+ */
+ if (coresight_get_mode(csdev) == CS_MODE_PERF) {
+ drvdata->sysfs_buf = NULL;
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+ /* Free allocated memory out side of the spinlock */
+ tmc_etr_free_sysfs_buf(sysfs_buf);
+ return -EBUSY;
+ }
+
+ /*
* In sysFS mode we can have multiple writers per sink. Since this
* sink is already enabled no memory is needed and the HW need not be
* touched, even if the buffer size has changed.
@@ -1354,9 +1367,7 @@ EXPORT_SYMBOL_GPL(tmc_etr_get_buffer);
/*
* alloc_etr_buf: Allocate ETR buffer for use by perf.
- * The size of the hardware buffer is dependent on the size configured
- * via sysfs and the perf ring buffer size. We prefer to allocate the
- * largest possible size, scaling down the size by half until it
+ * Allocate the largest possible size, scaling down the size by half until it
* reaches a minimum limit (1M), beyond which we give up.
*/
static struct etr_buf *
@@ -1365,36 +1376,26 @@ alloc_etr_buf(struct tmc_drvdata *drvdata, struct perf_event *event,
{
int node;
struct etr_buf *etr_buf;
- unsigned long size;
+ ssize_t size;
node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu);
- /*
- * Try to match the perf ring buffer size if it is larger
- * than the size requested via sysfs.
- */
- if ((nr_pages << PAGE_SHIFT) > drvdata->size) {
- etr_buf = tmc_alloc_etr_buf(drvdata, ((ssize_t)nr_pages << PAGE_SHIFT),
- 0, node, NULL);
- if (!IS_ERR(etr_buf))
- goto done;
- }
+
+ /* Use the minimum limit if the required size is smaller */
+ size = nr_pages << PAGE_SHIFT;
+ size = max_t(ssize_t, size, TMC_ETR_PERF_MIN_BUF_SIZE);
/*
- * Else switch to configured size for this ETR
- * and scale down until we hit the minimum limit.
+ * Try to allocate the required size for this ETR, if failed scale
+ * down until we hit the minimum limit.
*/
- size = drvdata->size;
do {
etr_buf = tmc_alloc_etr_buf(drvdata, size, 0, node, NULL);
if (!IS_ERR(etr_buf))
- goto done;
+ return etr_buf;
size /= 2;
} while (size >= TMC_ETR_PERF_MIN_BUF_SIZE);
return ERR_PTR(-ENOMEM);
-
-done:
- return etr_buf;
}
static struct etr_buf *