]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
coresight: etm3x: Always set tracer's device mode on target CPU
authorLeo Yan <leo.yan@arm.com>
Tue, 11 Nov 2025 18:58:37 +0000 (18:58 +0000)
committerSuzuki K Poulose <suzuki.poulose@arm.com>
Tue, 11 Nov 2025 21:47:58 +0000 (21:47 +0000)
The ETMv3 driver shares the same issue as ETMv4 regarding race
conditions when accessing the device mode.

This commit applies the same fix: ensuring that the device mode is
modified only by the target CPU to eliminate race conditions across
CPUs.

Fixes: 22fd532eaa0c ("coresight: etm3x: adding operation mode for etm_enable()")
Reviewed-by: Mike Leach <mike.leach@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Link: https://lore.kernel.org/r/20251111-arm_coresight_power_management_fix-v6-3-f55553b6c8b3@arm.com
drivers/hwtracing/coresight/coresight-etm3x-core.c

index 45630a1cd32fbd05ec8b2a6979f0174cacce365e..a5e809589d3e382acde24ee457e94e5dcb18ea35 100644 (file)
@@ -439,13 +439,26 @@ struct etm_enable_arg {
        int rc;
 };
 
-static void etm_enable_hw_smp_call(void *info)
+static void etm_enable_sysfs_smp_call(void *info)
 {
        struct etm_enable_arg *arg = info;
+       struct coresight_device *csdev;
 
        if (WARN_ON(!arg))
                return;
+
+       csdev = arg->drvdata->csdev;
+       if (!coresight_take_mode(csdev, CS_MODE_SYSFS)) {
+               /* Someone is already using the tracer */
+               arg->rc = -EBUSY;
+               return;
+       }
+
        arg->rc = etm_enable_hw(arg->drvdata);
+
+       /* The tracer didn't start */
+       if (arg->rc)
+               coresight_set_mode(csdev, CS_MODE_DISABLED);
 }
 
 static int etm_cpu_id(struct coresight_device *csdev)
@@ -465,16 +478,26 @@ static int etm_enable_perf(struct coresight_device *csdev,
                           struct coresight_path *path)
 {
        struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+       int ret;
 
        if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
                return -EINVAL;
 
+       if (!coresight_take_mode(csdev, CS_MODE_PERF))
+               return -EBUSY;
+
        /* Configure the tracer based on the session's specifics */
        etm_parse_event_config(drvdata, event);
        drvdata->traceid = path->trace_id;
 
        /* And enable it */
-       return etm_enable_hw(drvdata);
+       ret = etm_enable_hw(drvdata);
+
+       /* Failed to start tracer; roll back to DISABLED mode */
+       if (ret)
+               coresight_set_mode(csdev, CS_MODE_DISABLED);
+
+       return ret;
 }
 
 static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path)
@@ -494,7 +517,7 @@ static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_pat
        if (cpu_online(drvdata->cpu)) {
                arg.drvdata = drvdata;
                ret = smp_call_function_single(drvdata->cpu,
-                                              etm_enable_hw_smp_call, &arg, 1);
+                                              etm_enable_sysfs_smp_call, &arg, 1);
                if (!ret)
                        ret = arg.rc;
                if (!ret)
@@ -517,12 +540,6 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event,
                      enum cs_mode mode, struct coresight_path *path)
 {
        int ret;
-       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
-       if (!coresight_take_mode(csdev, mode)) {
-               /* Someone is already using the tracer */
-               return -EBUSY;
-       }
 
        switch (mode) {
        case CS_MODE_SYSFS:
@@ -535,17 +552,12 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event,
                ret = -EINVAL;
        }
 
-       /* The tracer didn't start */
-       if (ret)
-               coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
-
        return ret;
 }
 
-static void etm_disable_hw(void *info)
+static void etm_disable_hw(struct etm_drvdata *drvdata)
 {
        int i;
-       struct etm_drvdata *drvdata = info;
        struct etm_config *config = &drvdata->config;
        struct coresight_device *csdev = drvdata->csdev;
 
@@ -567,6 +579,15 @@ static void etm_disable_hw(void *info)
                "cpu: %d disable smp call done\n", drvdata->cpu);
 }
 
+static void etm_disable_sysfs_smp_call(void *info)
+{
+       struct etm_drvdata *drvdata = info;
+
+       etm_disable_hw(drvdata);
+
+       coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
+}
+
 static void etm_disable_perf(struct coresight_device *csdev)
 {
        struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -588,6 +609,8 @@ static void etm_disable_perf(struct coresight_device *csdev)
 
        CS_LOCK(drvdata->csa.base);
 
+       coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
+
        /*
         * perf will release trace ids when _free_aux()
         * is called at the end of the session
@@ -612,7 +635,8 @@ static void etm_disable_sysfs(struct coresight_device *csdev)
         * Executing etm_disable_hw on the cpu whose ETM is being disabled
         * ensures that register writes occur when cpu is powered.
         */
-       smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1);
+       smp_call_function_single(drvdata->cpu, etm_disable_sysfs_smp_call,
+                                drvdata, 1);
 
        spin_unlock(&drvdata->spinlock);
        cpus_read_unlock();
@@ -652,9 +676,6 @@ static void etm_disable(struct coresight_device *csdev,
                WARN_ON_ONCE(mode);
                return;
        }
-
-       if (mode)
-               coresight_set_mode(csdev, CS_MODE_DISABLED);
 }
 
 static const struct coresight_ops_source etm_source_ops = {