Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 22611bfe authored by Mathieu Poirier's avatar Mathieu Poirier Committed by Yabin Cui
Browse files

UPSTREAM: coresight: Move reference counting inside sink drivers



(Upstream commit f973d88b75703719d39c4d5145079199aaf442b2).

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: default avatarMathieu Poirier <mathieu.poirier@linaro.org>
Reviewed-by: default avatarSuzuki K Poulose <suzuki.poulose@arm.com>
Tested-by: default avatarLeo Yan <leo.yan@linaro.org>
Tested-by: default avatarRobert Walker <robert.walker@arm.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>

Bug: 140266694
Change-Id: Ia8dd776ddfb0576e62e44c556490b61defd4724c
Signed-off-by: default avatarYabin Cui <yabinc@google.com>
parent 7fb93829
Loading
Loading
Loading
Loading
+15 −6
Original line number Original line Diff line number Diff line
@@ -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>
@@ -159,14 +160,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;
@@ -196,8 +198,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);
@@ -332,6 +336,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);
+17 −4
Original line number Original line Diff line number Diff line
@@ -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>
@@ -183,8 +184,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
@@ -203,11 +206,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);


@@ -242,8 +247,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);


@@ -282,11 +289,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);
+16 −3
Original line number Original line Diff line number Diff line
@@ -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 −1
Original line number Original line Diff line number Diff line
@@ -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");
+13 −15
Original line number Original line Diff line number Diff line
@@ -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,15 +240,14 @@ 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,
				 struct coresight_device *parent,
				 struct coresight_device *parent,