[PATCH v7 20/20] cpufreq: CPPC: Create the FIE worker before enabling PCC callbacks
From: Christian Loehle
Date: Wed Sep 16 2026 - 13:17:57 EST
cppc_freq_invariance_init() creates the PCC worker only if an online CPU
currently has PCC counters. A shared policy can also initialize an offline
PCC member. When that CPU comes online, its callback queues work through
the missing kworker_fie and dereferences NULL. A later hot-added PCC policy
can encounter the same missing worker.
Create the worker when policy initialization encounters PCC counters,
before initializing their work and publishing any callbacks. Serialize
creation and reuse the worker until driver teardown. This covers offline
policy members and later PCC policies without creating an unused deadline
worker on direct-counter-only systems.
If worker allocation or scheduling setup fails, leave FIE unregistered for
that policy. Keep the global FIE setting unchanged so direct-counter
policies remain independent of worker setup and existing policies still
drain their initialized work during teardown. Publish kworker_fie only
after successful setup, allowing a later policy initialization to retry.
Fixes: 997c021abc6e ("cpufreq: CPPC: Update FIE arch_freq_scale in ticks for non-PCC regs")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Christian Loehle <christian.loehle@xxxxxxx>
---
drivers/cpufreq/cppc_cpufreq.c | 39 ++++++++++++++++++++--------------
1 file changed, 23 insertions(+), 16 deletions(-)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index c0fa9073e4d3..09e55bfdca88 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -18,6 +18,7 @@
#include <linux/cpufreq.h>
#include <linux/irq_work.h>
#include <linux/kthread.h>
+#include <linux/mutex.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
#include <uapi/linux/sched/types.h>
@@ -50,10 +51,12 @@ struct cppc_freq_invariance {
static DEFINE_PER_CPU(struct cppc_freq_invariance, cppc_freq_inv);
static struct kthread_worker *kworker_fie;
+static DEFINE_MUTEX(cppc_fie_lock);
static int cppc_perf_from_fbctrs(u64 reference_perf,
struct cppc_perf_fb_ctrs *fb_ctrs_t0,
struct cppc_perf_fb_ctrs *fb_ctrs_t1);
+static int cppc_fie_kworker_init(void);
/**
* __cppc_scale_freq_tick - CPPC arch_freq_scale updater for frequency invariance
@@ -162,6 +165,9 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
cppc_fi->cpu = cpu;
cppc_fi->cpu_data = policy->driver_data;
if (cppc_perf_ctrs_in_pcc_cpu(cpu)) {
+ if (cppc_fie_kworker_init())
+ return;
+
kthread_init_work(&cppc_fi->work, cppc_scale_freq_workfn);
init_irq_work(&cppc_fi->irq_work, cppc_irq_work);
cppc_fi->pcc_work_initialized = true;
@@ -216,8 +222,9 @@ static void cppc_cpufreq_cpu_fie_exit(struct cpufreq_policy *policy)
}
}
-static void cppc_fie_kworker_init(void)
+static int cppc_fie_kworker_init(void)
{
+ struct kthread_worker *worker;
struct sched_attr attr = {
.size = sizeof(struct sched_attr),
.sched_policy = SCHED_DEADLINE,
@@ -233,23 +240,28 @@ static void cppc_fie_kworker_init(void)
};
int ret;
- kworker_fie = kthread_run_worker(0, "cppc_fie");
- if (IS_ERR(kworker_fie)) {
+ guard(mutex)(&cppc_fie_lock);
+
+ if (kworker_fie)
+ return 0;
+
+ worker = kthread_run_worker(0, "cppc_fie");
+ if (IS_ERR(worker)) {
pr_warn("%s: failed to create kworker_fie: %ld\n", __func__,
- PTR_ERR(kworker_fie));
- fie_disabled = FIE_DISABLED;
- kworker_fie = NULL;
- return;
+ PTR_ERR(worker));
+ return PTR_ERR(worker);
}
- ret = sched_setattr_nocheck(kworker_fie->task, &attr);
+ ret = sched_setattr_nocheck(worker->task, &attr);
if (ret) {
pr_warn("%s: failed to set SCHED_DEADLINE: %d\n", __func__,
ret);
- kthread_destroy_worker(kworker_fie);
- fie_disabled = FIE_DISABLED;
- kworker_fie = NULL;
+ kthread_destroy_worker(worker);
+ return ret;
}
+
+ kworker_fie = worker;
+ return 0;
}
static void __init cppc_freq_invariance_init(void)
@@ -264,11 +276,6 @@ static void __init cppc_freq_invariance_init(void)
fie_disabled = FIE_ENABLED;
}
}
-
- if (fie_disabled || !perf_ctrs_in_pcc)
- return;
-
- cppc_fie_kworker_init();
}
static void cppc_freq_invariance_exit(void)
--
2.34.1