coresight: tmc-etr: Handle driver mode specific ETR buffers

Since the ETR could be driven either by SYSFS or by perf, it
becomes complicated how we deal with the buffers used for each
of these modes. The ETR driver cannot simply free the current
attached buffer without knowing the provider (i.e, sysfs vs perf).

To solve this issue, we provide:
1) the driver-mode specific etr buffer to be retained in the drvdata
2) the etr_buf for a session should be passed on when enabling the
   hardware, which will be stored in drvdata->etr_buf. This will be
   replaced (not free'd) as soon as the hardware is disabled, after
   necessary sync operation.

The advantages of this are :

1) The common code path doesn't need to worry about how to dispose
   an existing buffer, if it is about to start a new session with a
   different buffer, possibly in a different mode.
2) The driver mode can control its buffers and can get access to the
   saved session even when the hardware is operating in a different
   mode. (e.g, we can still access a trace buffer from a sysfs mode
   even if the etr is now used in perf mode, without disrupting the
   current session.)

Towards this, we introduce a sysfs specific data which will hold the
etr_buf used for sysfs mode of operation, controlled solely by the
sysfs mode handling code.

Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Suzuki K Poulose 2018-09-20 13:17:51 -06:00 committed by Greg Kroah-Hartman
parent 4f8ef21007
commit 96a7f64400
2 changed files with 40 additions and 20 deletions

View File

@ -895,10 +895,15 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata)
tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset); tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset);
} }
static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
struct etr_buf *etr_buf)
{ {
u32 axictl, sts; u32 axictl, sts;
struct etr_buf *etr_buf = drvdata->etr_buf;
/* Callers should provide an appropriate buffer for use */
if (WARN_ON(!etr_buf || drvdata->etr_buf))
return;
drvdata->etr_buf = etr_buf;
/* /*
* If this ETR is connected to a CATU, enable it before we turn * If this ETR is connected to a CATU, enable it before we turn
@ -960,13 +965,16 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
* also updating the @bufpp on where to find it. Since the trace data * also updating the @bufpp on where to find it. Since the trace data
* starts at anywhere in the buffer, depending on the RRP, we adjust the * starts at anywhere in the buffer, depending on the RRP, we adjust the
* @len returned to handle buffer wrapping around. * @len returned to handle buffer wrapping around.
*
* We are protected here by drvdata->reading != 0, which ensures the
* sysfs_buf stays alive.
*/ */
ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
loff_t pos, size_t len, char **bufpp) loff_t pos, size_t len, char **bufpp)
{ {
s64 offset; s64 offset;
ssize_t actual = len; ssize_t actual = len;
struct etr_buf *etr_buf = drvdata->etr_buf; struct etr_buf *etr_buf = drvdata->sysfs_buf;
if (pos + actual > etr_buf->len) if (pos + actual > etr_buf->len)
actual = etr_buf->len - pos; actual = etr_buf->len - pos;
@ -996,7 +1004,14 @@ tmc_etr_free_sysfs_buf(struct etr_buf *buf)
static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata) static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata)
{ {
tmc_sync_etr_buf(drvdata); struct etr_buf *etr_buf = drvdata->etr_buf;
if (WARN_ON(drvdata->sysfs_buf != etr_buf)) {
tmc_etr_free_sysfs_buf(drvdata->sysfs_buf);
drvdata->sysfs_buf = NULL;
} else {
tmc_sync_etr_buf(drvdata);
}
} }
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
@ -1017,6 +1032,8 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
/* Disable CATU device if this ETR is connected to one */ /* Disable CATU device if this ETR is connected to one */
tmc_etr_disable_catu(drvdata); tmc_etr_disable_catu(drvdata);
/* Reset the ETR buf used by hardware */
drvdata->etr_buf = NULL;
} }
static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
@ -1024,7 +1041,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
int ret = 0; int ret = 0;
unsigned long flags; unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
struct etr_buf *new_buf = NULL, *free_buf = NULL; struct etr_buf *sysfs_buf = NULL, *new_buf = NULL, *free_buf = NULL;
/* /*
* If we are enabling the ETR from disabled state, we need to make * If we are enabling the ETR from disabled state, we need to make
@ -1035,7 +1052,8 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
* with the lock released. * with the lock released.
*/ */
spin_lock_irqsave(&drvdata->spinlock, flags); spin_lock_irqsave(&drvdata->spinlock, flags);
if (!drvdata->etr_buf || (drvdata->etr_buf->size != drvdata->size)) { sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
if (!sysfs_buf || (sysfs_buf->size != drvdata->size)) {
spin_unlock_irqrestore(&drvdata->spinlock, flags); spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Allocate memory with the locks released */ /* Allocate memory with the locks released */
@ -1064,14 +1082,14 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
* If we don't have a buffer or it doesn't match the requested size, * If we don't have a buffer or it doesn't match the requested size,
* use the buffer allocated above. Otherwise reuse the existing buffer. * use the buffer allocated above. Otherwise reuse the existing buffer.
*/ */
if (!drvdata->etr_buf || sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
(new_buf && drvdata->etr_buf->size != new_buf->size)) { if (!sysfs_buf || (new_buf && sysfs_buf->size != new_buf->size)) {
free_buf = drvdata->etr_buf; free_buf = sysfs_buf;
drvdata->etr_buf = new_buf; drvdata->sysfs_buf = new_buf;
} }
drvdata->mode = CS_MODE_SYSFS; drvdata->mode = CS_MODE_SYSFS;
tmc_etr_enable_hw(drvdata); tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf);
out: out:
spin_unlock_irqrestore(&drvdata->spinlock, flags); spin_unlock_irqrestore(&drvdata->spinlock, flags);
@ -1156,13 +1174,13 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
goto out; goto out;
} }
/* If drvdata::etr_buf is NULL the trace data has been read already */ /* If sysfs_buf is NULL the trace data has been read already */
if (drvdata->etr_buf == NULL) { if (!drvdata->sysfs_buf) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
/* Disable the TMC if need be */ /* Disable the TMC if we are trying to read from a running session */
if (drvdata->mode == CS_MODE_SYSFS) if (drvdata->mode == CS_MODE_SYSFS)
tmc_etr_disable_hw(drvdata); tmc_etr_disable_hw(drvdata);
@ -1176,7 +1194,7 @@ out:
int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
{ {
unsigned long flags; unsigned long flags;
struct etr_buf *etr_buf = NULL; struct etr_buf *sysfs_buf = NULL;
/* config types are set a boot time and never change */ /* config types are set a boot time and never change */
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
@ -1191,22 +1209,22 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
* buffer. Since the tracer is still enabled drvdata::buf can't * buffer. Since the tracer is still enabled drvdata::buf can't
* be NULL. * be NULL.
*/ */
tmc_etr_enable_hw(drvdata); tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf);
} else { } else {
/* /*
* The ETR is not tracing and the buffer was just read. * The ETR is not tracing and the buffer was just read.
* As such prepare to free the trace buffer. * As such prepare to free the trace buffer.
*/ */
etr_buf = drvdata->etr_buf; sysfs_buf = drvdata->sysfs_buf;
drvdata->etr_buf = NULL; drvdata->sysfs_buf = NULL;
} }
drvdata->reading = false; drvdata->reading = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags); spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Free allocated memory out side of the spinlock */ /* Free allocated memory out side of the spinlock */
if (etr_buf) if (sysfs_buf)
tmc_free_etr_buf(etr_buf); tmc_etr_free_sysfs_buf(sysfs_buf);
return 0; return 0;
} }

View File

@ -170,6 +170,7 @@ struct etr_buf {
* @trigger_cntr: amount of words to store after a trigger. * @trigger_cntr: amount of words to store after a trigger.
* @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the * @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the
* device configuration register (DEVID) * device configuration register (DEVID)
* @sysfs_data: SYSFS buffer for ETR.
*/ */
struct tmc_drvdata { struct tmc_drvdata {
void __iomem *base; void __iomem *base;
@ -189,6 +190,7 @@ struct tmc_drvdata {
enum tmc_mem_intf_width memwidth; enum tmc_mem_intf_width memwidth;
u32 trigger_cntr; u32 trigger_cntr;
u32 etr_caps; u32 etr_caps;
struct etr_buf *sysfs_buf;
}; };
struct etr_buf_operations { struct etr_buf_operations {