Re: [PATCH] coresight: Serialize enabling/disabling a link device.

From: Suzuki K Poulose
Date: Fri Aug 09 2019 - 11:38:49 EST


Hi,-

On 08/08/2019 20:17, Yabin Cui wrote:
When tracing etm data of multiple threads on multiple cpus through perf
interface, some link devices are shared between paths of different cpus.
It creates race conditions when different cpus wants to enable/disable
the same link device at the same time.

Example 1:
Two cpus want to enable different ports of a coresight funnel, thus
calling the funnel enable operation at the same time. But the funnel
enable operation isn't reentrantable.

Example 2:
For an enabled coresight dynamic replicator with refcnt=1, one cpu wants
to disable it, while another cpu wants to enable it. Ideally we still have
an enabled replicator with refcnt=1 at the end. But in reality the result
is uncertain.

Since coresight devices claim themselves when enabled for self-hosted
usage, the race conditions above usually make the link devices not usable
after many cycles.

To fix the race conditions, this patch adds a spinlock to serialize
enabling/disabling a link device.

Signed-off-by: Yabin Cui <yabinc@xxxxxxxxxx>
---
drivers/hwtracing/coresight/coresight.c | 8 ++++++++
include/linux/coresight.h | 3 +++
2 files changed, 11 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index 55db77f6410b..90f97f4f99b2 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -256,6 +256,7 @@ static int coresight_enable_link(struct coresight_device *csdev,
int ret;
int link_subtype;
int refport, inport, outport;
+ unsigned long flags;
if (!parent || !child)
return -EINVAL;
@@ -274,15 +275,18 @@ static int coresight_enable_link(struct coresight_device *csdev,
if (refport < 0)
return refport;
+ spin_lock_irqsave(&csdev->spinlock, flags);
if (atomic_inc_return(&csdev->refcnt[refport]) == 1) {
if (link_ops(csdev)->enable) {
ret = link_ops(csdev)->enable(csdev, inport, outport);
if (ret) {
atomic_dec(&csdev->refcnt[refport]);
+ spin_unlock_irqrestore(&csdev->spinlock, flags);
return ret;
}
}
}
+ spin_unlock_irqrestore(&csdev->spinlock, flags);
csdev->enable = true;

@@ -296,6 +300,7 @@ static void coresight_disable_link(struct coresight_device *csdev,
int i, nr_conns;
int link_subtype;
int refport, inport, outport;
+ unsigned long flags;
if (!parent || !child)
return;
@@ -315,10 +320,12 @@ static void coresight_disable_link(struct coresight_device *csdev,
nr_conns = 1;
}
+ spin_lock_irqsave(&csdev->spinlock, flags);
if (atomic_dec_return(&csdev->refcnt[refport]) == 0) {
if (link_ops(csdev)->disable)
link_ops(csdev)->disable(csdev, inport, outport);
}
+ spin_unlock_irqrestore(&csdev->spinlock, flags);

You may also want to protect the refcount checks below with the same lock, just
to be consistent.

With that :

Reviewed-by: Suzuki K Poulose <suzuki.poulose@xxxxxxx>