coresight: Move reference counting inside sink drivers
When operating in CPU-wide mode with an N:1 source/sink HW topology, multiple CPUs can access a sink concurrently. As such reference counting needs to happen when the device's spinlock is held to avoid racing with other operations (start(), update(), stop()), such as: session A Session B ----- ------- enable_sink atomic_inc(refcount) = 1 ... atomic_dec(refcount) = 0 enable_sink if (refcount == 0) disable_sink atomic_inc() Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com> Tested-by: Leo Yan <leo.yan@linaro.org> Tested-by: Robert Walker <robert.walker@arm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
6c817a95d8
commit
f973d88b75
|
@ -5,6 +5,7 @@
|
||||||
* Description: CoreSight Embedded Trace Buffer driver
|
* Description: CoreSight Embedded Trace Buffer driver
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/atomic.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
@ -151,14 +152,15 @@ static int etb_enable_sysfs(struct coresight_device *csdev)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nothing to do, the tracer is already enabled. */
|
if (drvdata->mode == CS_MODE_DISABLED) {
|
||||||
if (drvdata->mode == CS_MODE_SYSFS)
|
ret = etb_enable_hw(drvdata);
|
||||||
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = etb_enable_hw(drvdata);
|
|
||||||
if (!ret)
|
|
||||||
drvdata->mode = CS_MODE_SYSFS;
|
drvdata->mode = CS_MODE_SYSFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_inc(csdev->refcnt);
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -188,8 +190,10 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = etb_enable_hw(drvdata);
|
ret = etb_enable_hw(drvdata);
|
||||||
if (!ret)
|
if (!ret) {
|
||||||
drvdata->mode = CS_MODE_PERF;
|
drvdata->mode = CS_MODE_PERF;
|
||||||
|
atomic_inc(csdev->refcnt);
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
|
@ -324,6 +328,11 @@ static int etb_disable(struct coresight_device *csdev)
|
||||||
|
|
||||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||||
|
|
||||||
|
if (atomic_dec_return(csdev->refcnt)) {
|
||||||
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
/* Disable the ETB only if it needs to */
|
/* Disable the ETB only if it needs to */
|
||||||
if (drvdata->mode != CS_MODE_DISABLED) {
|
if (drvdata->mode != CS_MODE_DISABLED) {
|
||||||
etb_disable_hw(drvdata);
|
etb_disable_hw(drvdata);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/atomic.h>
|
||||||
#include <linux/circ_buf.h>
|
#include <linux/circ_buf.h>
|
||||||
#include <linux/coresight.h>
|
#include <linux/coresight.h>
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
|
@ -180,8 +181,10 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
|
||||||
* sink is already enabled no memory is needed and the HW need not be
|
* sink is already enabled no memory is needed and the HW need not be
|
||||||
* touched.
|
* touched.
|
||||||
*/
|
*/
|
||||||
if (drvdata->mode == CS_MODE_SYSFS)
|
if (drvdata->mode == CS_MODE_SYSFS) {
|
||||||
|
atomic_inc(csdev->refcnt);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If drvdata::buf isn't NULL, memory was allocated for a previous
|
* If drvdata::buf isn't NULL, memory was allocated for a previous
|
||||||
|
@ -200,11 +203,13 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = tmc_etb_enable_hw(drvdata);
|
ret = tmc_etb_enable_hw(drvdata);
|
||||||
if (!ret)
|
if (!ret) {
|
||||||
drvdata->mode = CS_MODE_SYSFS;
|
drvdata->mode = CS_MODE_SYSFS;
|
||||||
else
|
atomic_inc(csdev->refcnt);
|
||||||
|
} else {
|
||||||
/* Free up the buffer if we failed to enable */
|
/* Free up the buffer if we failed to enable */
|
||||||
used = false;
|
used = false;
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
|
|
||||||
|
@ -239,8 +244,10 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
ret = tmc_etb_enable_hw(drvdata);
|
ret = tmc_etb_enable_hw(drvdata);
|
||||||
if (!ret)
|
if (!ret) {
|
||||||
drvdata->mode = CS_MODE_PERF;
|
drvdata->mode = CS_MODE_PERF;
|
||||||
|
atomic_inc(csdev->refcnt);
|
||||||
|
}
|
||||||
} while (0);
|
} while (0);
|
||||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
|
|
||||||
|
@ -279,11 +286,17 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
|
||||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||||
|
|
||||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||||
|
|
||||||
if (drvdata->reading) {
|
if (drvdata->reading) {
|
||||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (atomic_dec_return(csdev->refcnt)) {
|
||||||
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
/* Disable the TMC only if it needs to */
|
/* Disable the TMC only if it needs to */
|
||||||
if (drvdata->mode != CS_MODE_DISABLED) {
|
if (drvdata->mode != CS_MODE_DISABLED) {
|
||||||
tmc_etb_disable_hw(drvdata);
|
tmc_etb_disable_hw(drvdata);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/atomic.h>
|
||||||
#include <linux/coresight.h>
|
#include <linux/coresight.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/iommu.h>
|
#include <linux/iommu.h>
|
||||||
|
@ -1125,8 +1126,10 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
||||||
* sink is already enabled no memory is needed and the HW need not be
|
* sink is already enabled no memory is needed and the HW need not be
|
||||||
* touched, even if the buffer size has changed.
|
* touched, even if the buffer size has changed.
|
||||||
*/
|
*/
|
||||||
if (drvdata->mode == CS_MODE_SYSFS)
|
if (drvdata->mode == CS_MODE_SYSFS) {
|
||||||
|
atomic_inc(csdev->refcnt);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
||||||
|
@ -1139,8 +1142,10 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf);
|
ret = tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf);
|
||||||
if (!ret)
|
if (!ret) {
|
||||||
drvdata->mode = CS_MODE_SYSFS;
|
drvdata->mode = CS_MODE_SYSFS;
|
||||||
|
atomic_inc(csdev->refcnt);
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
|
|
||||||
|
@ -1371,8 +1376,10 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
|
||||||
etr_perf->head = PERF_IDX2OFF(handle->head, etr_perf);
|
etr_perf->head = PERF_IDX2OFF(handle->head, etr_perf);
|
||||||
drvdata->perf_data = etr_perf;
|
drvdata->perf_data = etr_perf;
|
||||||
rc = tmc_etr_enable_hw(drvdata, etr_perf->etr_buf);
|
rc = tmc_etr_enable_hw(drvdata, etr_perf->etr_buf);
|
||||||
if (!rc)
|
if (!rc) {
|
||||||
drvdata->mode = CS_MODE_PERF;
|
drvdata->mode = CS_MODE_PERF;
|
||||||
|
atomic_inc(csdev->refcnt);
|
||||||
|
}
|
||||||
|
|
||||||
unlock_out:
|
unlock_out:
|
||||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
|
@ -1399,11 +1406,17 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
|
||||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||||
|
|
||||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||||
|
|
||||||
if (drvdata->reading) {
|
if (drvdata->reading) {
|
||||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (atomic_dec_return(csdev->refcnt)) {
|
||||||
|
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
/* Disable the TMC only if it needs to */
|
/* Disable the TMC only if it needs to */
|
||||||
if (drvdata->mode != CS_MODE_DISABLED) {
|
if (drvdata->mode != CS_MODE_DISABLED) {
|
||||||
tmc_etr_disable_hw(drvdata);
|
tmc_etr_disable_hw(drvdata);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* Description: CoreSight Trace Port Interface Unit driver
|
* Description: CoreSight Trace Port Interface Unit driver
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/atomic.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
@ -73,7 +74,7 @@ static int tpiu_enable(struct coresight_device *csdev, u32 mode, void *__unused)
|
||||||
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||||
|
|
||||||
tpiu_enable_hw(drvdata);
|
tpiu_enable_hw(drvdata);
|
||||||
|
atomic_inc(csdev->refcnt);
|
||||||
dev_dbg(drvdata->dev, "TPIU enabled\n");
|
dev_dbg(drvdata->dev, "TPIU enabled\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +99,9 @@ static int tpiu_disable(struct coresight_device *csdev)
|
||||||
{
|
{
|
||||||
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||||
|
|
||||||
|
if (atomic_dec_return(csdev->refcnt))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
tpiu_disable_hw(drvdata);
|
tpiu_disable_hw(drvdata);
|
||||||
|
|
||||||
dev_dbg(drvdata->dev, "TPIU disabled\n");
|
dev_dbg(drvdata->dev, "TPIU disabled\n");
|
||||||
|
|
|
@ -225,14 +225,13 @@ static int coresight_enable_sink(struct coresight_device *csdev,
|
||||||
* We need to make sure the "new" session is compatible with the
|
* We need to make sure the "new" session is compatible with the
|
||||||
* existing "mode" of operation.
|
* existing "mode" of operation.
|
||||||
*/
|
*/
|
||||||
if (sink_ops(csdev)->enable) {
|
if (!sink_ops(csdev)->enable)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
ret = sink_ops(csdev)->enable(csdev, mode, data);
|
ret = sink_ops(csdev)->enable(csdev, mode, data);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
csdev->enable = true;
|
csdev->enable = true;
|
||||||
}
|
|
||||||
|
|
||||||
atomic_inc(csdev->refcnt);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -241,14 +240,13 @@ static void coresight_disable_sink(struct coresight_device *csdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (atomic_dec_return(csdev->refcnt) == 0) {
|
if (!sink_ops(csdev)->disable)
|
||||||
if (sink_ops(csdev)->disable) {
|
return;
|
||||||
|
|
||||||
ret = sink_ops(csdev)->disable(csdev);
|
ret = sink_ops(csdev)->disable(csdev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return;
|
||||||
csdev->enable = false;
|
csdev->enable = false;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coresight_enable_link(struct coresight_device *csdev,
|
static int coresight_enable_link(struct coresight_device *csdev,
|
||||||
|
|
Loading…
Reference in New Issue