Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: James Clark <james.clark@linaro.org>
To: Suzuki K Poulose <suzuki.poulose@arm.com>,
	 Mike Leach <mike.leach@arm.com>, Leo Yan <leo.yan@arm.com>,
	 Suyash Mahar <smahar@meta.com>,
	Yeoreum Yun <yeoreum.yun@arm.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	 Qi Liu <liuqi115@huawei.com>, Junhao He <hejunhao3@huawei.com>,
	 coresight@lists.linaro.org,
	linux-arm-kernel@lists.infradead.org,
	 linux-kernel@vger.kernel.org,
	James Clark <james.clark@linaro.org>,
	 Jonathan Cameron <jic23@kernel.org>,
	sashiko-bot <sashiko-bot@kernel.org>
Subject: [PATCH v3 1/8] coresight: tmc-etr: Don't stop Perf cleanup for active sysfs reads
Date: Tue, 28 Jul 2026 16:00:13 +0100	[thread overview]
Message-ID: <20260728-james-cs-multiple-per-threads-v3-1-6aee7579f1dc@linaro.org> (raw)
In-Reply-To: <20260728-james-cs-multiple-per-threads-v3-0-6aee7579f1dc@linaro.org>

The linked fixes commit deliberately allows reads of an old sysfs buffer
while in Perf mode because they are separate software buffers. However
it didn't modify tmc_disable_etr_sink() to match this relaxation. The
result is that when a Perf event ends while the sysfs buffer is being
read, clean up will be skipped.

Fix it by ignoring the sysfs_reading flag unless the active session is
a sysfs one.

When sysfs and Perf share the same memory in ETR_MODE_RESRV mode, a new
Perf session needs to overwrite an old inactive sysfs session by zeroing
len. This avoids sysfs from reading stale data because it has a separate
set of offsets in its etr_buf struct, even if that's backed by the same
memory as the Perf one.

Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Fixes: cad5f8d399bb ("coresight: tmc-etr: Relax collection of trace from sysfs mode")
Signed-off-by: James Clark <james.clark@linaro.org>
---
 drivers/hwtracing/coresight/coresight-tmc-etf.c | 16 ++++----
 drivers/hwtracing/coresight/coresight-tmc-etr.c | 50 +++++++++++++++++++++----
 drivers/hwtracing/coresight/coresight-tmc.h     |  7 +++-
 3 files changed, 55 insertions(+), 18 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 8882b1c4cdc0..3836063031d7 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -198,7 +198,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
 		raw_spin_lock_irqsave(&drvdata->spinlock, flags);
 	}
 
-	if (drvdata->reading) {
+	if (drvdata->sysfs_reading) {
 		ret = -EBUSY;
 		goto out;
 	}
@@ -259,7 +259,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev,
 	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
 	do {
 		ret = -EINVAL;
-		if (drvdata->reading)
+		if (drvdata->sysfs_reading)
 			break;
 		/*
 		 * No need to continue if the ETB/ETF is already operated
@@ -337,7 +337,7 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
 
 	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	if (drvdata->reading) {
+	if (drvdata->sysfs_reading) {
 		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
 		return -EBUSY;
 	}
@@ -371,7 +371,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
 	bool first_enable = false;
 
 	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (drvdata->reading) {
+	if (drvdata->sysfs_reading) {
 		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
 		return -EBUSY;
 	}
@@ -401,7 +401,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
 	bool last_disable = false;
 
 	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (drvdata->reading) {
+	if (drvdata->sysfs_reading) {
 		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
 		return;
 	}
@@ -718,7 +718,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
 
 	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	if (drvdata->reading) {
+	if (drvdata->sysfs_reading) {
 		ret = -EBUSY;
 		goto out;
 	}
@@ -746,7 +746,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
 		__tmc_etb_disable_hw(drvdata);
 	}
 
-	drvdata->reading = true;
+	drvdata->sysfs_reading = true;
 out:
 	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
@@ -797,7 +797,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
 		drvdata->buf = NULL;
 	}
 
-	drvdata->reading = false;
+	drvdata->sysfs_reading = false;
 	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	/*
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 361a433e6f0c..be0bbe036d02 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1151,7 +1151,7 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
  * starts at anywhere in the buffer, depending on the RRP, we adjust the
  * @len returned to handle buffer wrapping around.
  *
- * We are protected here by drvdata->reading != 0, which ensures the
+ * We are protected here by drvdata->sysfs_reading != 0, which ensures the
  * sysfs_buf stays alive.
  */
 ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
@@ -1268,7 +1268,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
 		raw_spin_lock_irqsave(&drvdata->spinlock, flags);
 	}
 
-	if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) {
+	if (drvdata->sysfs_reading || coresight_get_mode(csdev) == CS_MODE_PERF) {
 		ret = -EBUSY;
 		goto out;
 	}
@@ -1732,6 +1732,16 @@ tmc_update_etr_buffer(struct coresight_device *csdev,
 	return size;
 }
 
+static bool tmc_perf_sysfs_shared(struct tmc_drvdata *drvdata,
+				  struct etr_buf *perf_buf)
+{
+	/* In ETR_MODE_RESRV mode, sysfs and Perf share the same memory. */
+	return perf_buf &&
+	       drvdata->sysfs_buf &&
+	       drvdata->sysfs_buf->mode == ETR_MODE_RESRV &&
+	       perf_buf->mode == ETR_MODE_RESRV;
+}
+
 static int tmc_enable_etr_sink_perf(struct coresight_device *csdev,
 				    struct coresight_path *path)
 {
@@ -1772,6 +1782,18 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev,
 		goto unlock_out;
 	}
 
+	/*
+	 * Don't use if it's shared and being read by sysfs. Sysfs may only
+	 * start reading (the cleared zero length buffer) after the first
+	 * tmc_enable_etr_sink_perf(), which changes the result of this check,
+	 * so it should only be done once.
+	 */
+	if ((drvdata->sysfs_reading &&
+	     tmc_perf_sysfs_shared(drvdata, etr_perf->etr_buf))) {
+		rc = -EBUSY;
+		goto unlock_out;
+	}
+
 	rc = tmc_etr_enable_hw(drvdata, etr_perf->etr_buf);
 	if (!rc) {
 		/* Associate with monitored process. */
@@ -1779,6 +1801,10 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev,
 		coresight_set_mode(csdev, CS_MODE_PERF);
 		drvdata->perf_buf = etr_perf->etr_buf;
 		csdev->refcnt++;
+
+		/* A new Perf session clears an old sysfs one if the buffer is shared */
+		if (tmc_perf_sysfs_shared(drvdata, etr_perf->etr_buf))
+			drvdata->sysfs_buf->len = 0;
 	}
 
 unlock_out:
@@ -1807,7 +1833,13 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
 
 	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
 
-	if (drvdata->reading) {
+	/*
+	 * In SYSFS mode an active read is responsible for disabling and
+	 * enabling HW. Otherwise in Perf mode, an old inactive sysfs session
+	 * may be read which Perf should ignore.
+	 */
+	if (drvdata->sysfs_reading &&
+	    coresight_get_mode(csdev) == CS_MODE_SYSFS) {
 		raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
 		return -EBUSY;
 	}
@@ -1928,14 +1960,16 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 		return -EINVAL;
 
 	raw_spin_lock_irqsave(&drvdata->spinlock, flags);
-	if (drvdata->reading) {
+	if (drvdata->sysfs_reading) {
 		ret = -EBUSY;
 		goto out;
 	}
 
 	/*
-	 * We can safely allow reads even if the ETR is operating in PERF mode,
-	 * since the sysfs session is captured in mode specific data.
+	 * We can safely allow reads even if the ETR is operating in PERF mode
+	 * since sysfs has it's own buffer. For ETR_MODE_RESRV the buffers are
+	 * shared but Perf discards sysfs data before starting a session to
+	 * avoid corruption.
 	 * If drvdata::sysfs_data is NULL the trace data has been read already.
 	 */
 	if (!drvdata->sysfs_buf) {
@@ -1947,7 +1981,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
 	if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
 		__tmc_etr_disable_hw(drvdata);
 
-	drvdata->reading = true;
+	drvdata->sysfs_reading = true;
 out:
 	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
@@ -1982,7 +2016,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
 		drvdata->sysfs_buf = NULL;
 	}
 
-	drvdata->reading = false;
+	drvdata->sysfs_reading = false;
 	raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
 
 	/* Free allocated memory out side of the spinlock */
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 319a354ede9f..dc1a57ab8011 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -221,7 +221,10 @@ struct tmc_resrv_buf {
  * @pid:	Process ID of the process that owns the session that is using
  *		this component. For example this would be the pid of the Perf
  *		process.
- * @reading:	buffer's in the reading through "/dev/xyz.tmc" entry
+ * @sysfs_reading: Sysfs mode buffer is being read through "/dev/xyz.tmc" entry.
+ *                Note: ETR has a separate software buffer for the two modes so
+ *		  the device can still be read while in Perf mode if there is a
+ *		  previous inactive sysfs session.
  * @stop_on_flush: Stop on flush trigger user configuration.
  * @buf:	Snapshot of the trace data for ETF/ETB.
  * @etr_buf:	details of buffer used in TMC-ETR
@@ -255,7 +258,7 @@ struct tmc_drvdata {
 	struct miscdevice	crashdev;
 	raw_spinlock_t		spinlock;
 	pid_t			pid;
-	bool			reading;
+	bool			sysfs_reading;
 	bool			stop_on_flush;
 	union {
 		char		*buf;		/* TMC ETB */

-- 
2.34.1



  reply	other threads:[~2026-07-28 15:01 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-28 15:00 [PATCH v3 0/8] coresight: Prevent per-thread events from sharing a sink James Clark
2026-07-28 15:00 ` James Clark [this message]
2026-08-12 16:45   ` [PATCH v3 1/8] coresight: tmc-etr: Don't stop Perf cleanup for active sysfs reads Leo Yan
2026-08-13  9:02     ` James Clark
2026-07-28 15:00 ` [PATCH v3 2/8] coresight: configfs: Don't assume active until cscfg_mgr is set James Clark
2026-08-13  9:09   ` Leo Yan
2026-08-13  9:24     ` James Clark
2026-07-28 15:00 ` [PATCH v3 3/8] coresight: etm-perf: Flush workqueue before unloading module James Clark
2026-08-13 14:19   ` Leo Yan
2026-08-14  8:55     ` James Clark
2026-07-28 15:00 ` [PATCH v3 4/8] coresight: tmc-etr: Prevent per-thread events from sharing a sink James Clark
2026-08-13 16:05   ` Leo Yan
2026-08-14  9:09     ` James Clark
2026-08-19  8:45       ` Leo Yan
2026-08-20 11:09         ` James Clark
2026-08-20 11:27           ` James Clark
2026-08-25 17:32           ` Leo Yan
2026-08-26  8:40             ` James Clark
2026-07-28 15:00 ` [PATCH v3 5/8] coresight: tmc-etr: Use session ID for buffer ownership James Clark
2026-07-28 15:00 ` [PATCH v3 6/8] coresight: tmc-etf: Prevent per-thread events from sharing a sink James Clark
2026-07-28 15:00 ` [PATCH v3 7/8] coresight: etb10: " James Clark
2026-07-28 15:00 ` [PATCH v3 8/8] coresight: ultrasoc-smb: " James Clark

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260728-james-cs-multiple-per-threads-v3-1-6aee7579f1dc@linaro.org \
    --to=james.clark@linaro.org \
    --cc=coresight@lists.linaro.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=hejunhao3@huawei.com \
    --cc=jic23@kernel.org \
    --cc=leo.yan@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liuqi115@huawei.com \
    --cc=mike.leach@arm.com \
    --cc=sashiko-bot@kernel.org \
    --cc=smahar@meta.com \
    --cc=suzuki.poulose@arm.com \
    --cc=yeoreum.yun@arm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox