[PATCH v3 3/8] coresight: etm-perf: Flush workqueue before unloading module

James Clark james.clark at linaro.org
Tue Jul 28 08:00:15 PDT 2026


The work queue is never flushed on module unload, so pending work items
can run after coresight-config is cleaned up or the .text section is
unloaded. The global workqueue also can't be flushed manually, so change
it to a local workqueue and flush it on exit.

The init function needs to be rearranged to fix a similar problem. The
Perf PMU can be used as soon as registration succeeds, so move it as
late as possible so that the only failure that can follow is coresight-
config. Failing to register coresight-config means no flush dependency
between coresight-config and the Perf PMU will exist, so it doesn't need
to be done.

Assisted-by: Codex:GPT-5.6-Sol
Reported-by: sashiko-bot <sashiko-bot at kernel.org>
Fixes: 0bcbf2e30ff2 ("coresight: etm-perf: new PMU driver for ETM tracers")
Signed-off-by: James Clark <james.clark at linaro.org>
---
 drivers/hwtracing/coresight/coresight-core.c     | 28 ++++++++++++++----------
 drivers/hwtracing/coresight/coresight-etm-perf.c | 22 +++++++++++++++----
 drivers/hwtracing/coresight/coresight-etm-perf.h |  1 +
 3 files changed, 36 insertions(+), 15 deletions(-)

diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index f7b1308a759c..5e653b08a83d 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -2094,31 +2094,31 @@ static int __init coresight_init(void)
 	if (ret)
 		return ret;
 
-	ret = etm_perf_init();
-	if (ret)
-		goto exit_bus_unregister;
-
 	/* Register function to be called for panic */
 	ret = atomic_notifier_chain_register(&panic_notifier_list,
 					     &coresight_notifier);
 	if (ret)
-		goto exit_perf;
+		goto exit_bus_unregister;
 
-	/* initialise the coresight syscfg API */
-	ret = cscfg_init();
+	ret = coresight_pm_setup();
 	if (ret)
 		goto exit_notifier;
 
-	ret = coresight_pm_setup();
+	ret = etm_perf_init();
+	if (ret)
+		goto exit_pm;
+
+	/* initialise the coresight syscfg API */
+	ret = cscfg_init();
 	if (!ret)
 		return 0;
 
-	cscfg_exit();
+	etm_perf_exit();
+exit_pm:
+	coresight_pm_cleanup();
 exit_notifier:
 	atomic_notifier_chain_unregister(&panic_notifier_list,
 					     &coresight_notifier);
-exit_perf:
-	etm_perf_exit();
 exit_bus_unregister:
 	bus_unregister(&coresight_bustype);
 	return ret;
@@ -2127,6 +2127,12 @@ static int __init coresight_init(void)
 static void __exit coresight_exit(void)
 {
 	coresight_pm_cleanup();
+	/*
+	 * Flush Perf workqueue before the rest of Coresight is torn down
+	 * because work items touch coresight-config and also require the .text
+	 * to remain loaded.
+	 */
+	etm_perf_flush_workqueue();
 	cscfg_exit();
 	atomic_notifier_chain_unregister(&panic_notifier_list,
 					     &coresight_notifier);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 09b21a711a87..7fb5c3d18bd5 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -27,6 +27,7 @@
 #include "coresight-trace-id.h"
 
 static struct pmu etm_pmu;
+static struct workqueue_struct *etm_free_wq;
 static bool etm_perf_up;
 
 /*
@@ -292,7 +293,7 @@ static void etm_free_aux(void *data)
 {
 	struct etm_event_data *event_data = data;
 
-	schedule_work(&event_data->work);
+	queue_work(etm_free_wq, &event_data->work);
 }
 
 /*
@@ -1034,6 +1035,10 @@ int __init etm_perf_init(void)
 {
 	int ret;
 
+	etm_free_wq = alloc_workqueue("coresight_etm_free", WQ_UNBOUND, 0);
+	if (!etm_free_wq)
+		return -ENOMEM;
+
 	etm_pmu.capabilities		= (PERF_PMU_CAP_EXCLUSIVE |
 					   PERF_PMU_CAP_ITRACE |
 					   PERF_PMU_CAP_AUX_PAUSE);
@@ -1054,13 +1059,22 @@ int __init etm_perf_init(void)
 	etm_pmu.module			= THIS_MODULE;
 
 	ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
-	if (ret == 0)
-		etm_perf_up = true;
+	if (ret) {
+		destroy_workqueue(etm_free_wq);
+		return ret;
+	}
 
-	return ret;
+	etm_perf_up = true;
+	return 0;
 }
 
 void etm_perf_exit(void)
 {
 	perf_pmu_unregister(&etm_pmu);
+	destroy_workqueue(etm_free_wq);
+}
+
+void etm_perf_flush_workqueue(void)
+{
+	flush_workqueue(etm_free_wq);
 }
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h
index 24d929428633..86e259cc1adf 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.h
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.h
@@ -116,5 +116,6 @@ int etm_perf_add_symlink_cscfg(struct device *dev,
 void etm_perf_del_symlink_cscfg(struct cscfg_config_desc *config_desc);
 int __init etm_perf_init(void);
 void etm_perf_exit(void);
+void etm_perf_flush_workqueue(void);
 
 #endif

-- 
2.34.1




More information about the linux-arm-kernel mailing list