[PATCH v3 3/8] coresight: etm-perf: Flush workqueue before unloading module
From: James Clark
Date: Tue Jul 28 2026 - 11:23:16 EST
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@xxxxxxxxxx>
Fixes: 0bcbf2e30ff2 ("coresight: etm-perf: new PMU driver for ETM tracers")
Signed-off-by: James Clark <james.clark@xxxxxxxxxx>
---
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