Linux Power Management development
 help / color / mirror / Atom feed
From: Hungyu Lin <dennylin0707@gmail.com>
To: zouyipeng@huawei.com
Cc: zhoujinghao24b@gmail.com, rafael@kernel.org,
	viresh.kumar@linaro.org, linux-pm@vger.kernel.org,
	bpf@vger.kernel.org
Subject: [RFC PATCH] cpufreq: ext: serialize BPF registration with governor teardown
Date: Fri, 23 Oct 2026 08:31:31 +0000	[thread overview]
Message-ID: <20261023083131.58291-1-dennylin0707@gmail.com> (raw)
In-Reply-To: <20240927101342.3240263-2-zouyipeng@huawei.com>

The struct_ops link can outlive the last policy using the ext governor.
Switching that policy to performance releases the DBS tuners and clears
ext_global_tuner. Destroying the link afterwards calls ext_disable(),
which unconditionally dereferences the NULL pointer.

Serialize tuner publication and release with BPF registration and
unregistration using ext_mutex, and allow unregistering after the tuners
have gone away. Preserve registered BPF operations when the governor is
initialized again while the link is still alive.

Take the CPU hotplug read lock before ext_mutex in the BPF callbacks and
use the cpuslocked static-key helpers. Governor callbacks can run under
the hotplug write lock, so acquiring the read lock inside ext_mutex would
invert their lock order.

Reproduce by selecting ext, attaching the RFC sample, selecting
performance, then destroying the link. This was tested on arm64 QEMU
with Linux 6.12 plus the RFC and Jinghao Zhou's ext_gov_update() fix.
Lifecycle, frequency-limit and original sample checks also passed with
lockdep, jump labels and CPU hotplug enabled.

The 2025 fix addresses a separate policy-state lookup error. This
teardown bug is present in the original, unmerged RFC as well.

Link: https://lore.kernel.org/linux-pm/20240927101342.3240263-2-zouyipeng@huawei.com/
Link: https://lore.kernel.org/linux-pm/20250918125241.80968-1-zhoujinghao24b@gmail.com/
Signed-off-by: Hungyu Lin <dennylin0707@gmail.com>
---
 drivers/cpufreq/cpufreq_ext.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/cpufreq/cpufreq_ext.c b/drivers/cpufreq/cpufreq_ext.c
index c79068e86..20d4d3648 100644
--- a/drivers/cpufreq/cpufreq_ext.c
+++ b/drivers/cpufreq/cpufreq_ext.c
@@ -197,8 +197,9 @@ static int ext_check_member(const struct btf_type *t,
 static void ext_disable(void)
 {
 	pr_info("%s : Unregister ext governor(%s).\n", __func__, ext_ops_global.name);
-	static_branch_disable(&ext_gov_load);
-	ext_global_tuner->ext_gov_stat = CPUFREQ_EXT_INIT;
+	static_branch_disable_cpuslocked(&ext_gov_load);
+	if (ext_global_tuner)
+		ext_global_tuner->ext_gov_stat = CPUFREQ_EXT_INIT;
 	ext_ops_global.get_next_freq = get_next_freq_nop;
 	ext_ops_global.get_sampling_rate = get_sampling_rate_nop;
 	ext_ops_global.init = init_nop;
@@ -210,6 +211,7 @@ static int ext_reg(void *kdata, struct bpf_link *link)
 {
 	struct cpufreq_governor_ext_ops *ops = (struct cpufreq_governor_ext_ops *)kdata;
 
+	cpus_read_lock();
 	mutex_lock(&ext_mutex);
 
 	/*
@@ -218,12 +220,14 @@ static int ext_reg(void *kdata, struct bpf_link *link)
 	if (ext_global_tuner == NULL) {
 		pr_err("%s : Need set ext governor first.\n", __func__);
 		mutex_unlock(&ext_mutex);
+		cpus_read_unlock();
 		return -EEXIST;
 	}
 
 	if (ext_global_tuner->ext_gov_stat != CPUFREQ_EXT_INIT) {
 		pr_err("%s : Already register.\n", __func__);
 		mutex_unlock(&ext_mutex);
+		cpus_read_unlock();
 		return -EEXIST;
 	}
 
@@ -233,17 +237,20 @@ static int ext_reg(void *kdata, struct bpf_link *link)
 	if (ext_ops_global.init && ext_ops_global.init()) {
 		ext_disable();
 		mutex_unlock(&ext_mutex);
+		cpus_read_unlock();
 		return -EINVAL;
 	}
 
-	static_branch_enable(&ext_gov_load);
+	static_branch_enable_cpuslocked(&ext_gov_load);
 	pr_info("%s: Register ext governor(%s).\n", __func__, ext_ops_global.name);
 	mutex_unlock(&ext_mutex);
+	cpus_read_unlock();
 	return 0;
 }
 
 static void ext_unreg(void *kdata, struct bpf_link *link)
 {
+	cpus_read_lock();
 	mutex_lock(&ext_mutex);
 
 	if (ext_ops_global.exit)
@@ -251,6 +258,7 @@ static void ext_unreg(void *kdata, struct bpf_link *link)
 
 	ext_disable();
 	mutex_unlock(&ext_mutex);
+	cpus_read_unlock();
 }
 
 static int ext_init(struct btf *btf)
@@ -470,9 +478,13 @@ static int ext_gov_init(struct dbs_data *dbs_data)
 	dbs_data->ignore_nice_load = 0;
 	dbs_data->tuners = tuner;
 	dbs_data->up_threshold = DEAFULT_FREQUENCY_UP_THRESHOLD;
+
+	mutex_lock(&ext_mutex);
 	ext_global_tuner = tuner;
-	/* Disable ext_gov_load as default */
-	static_branch_disable(&ext_gov_load);
+	if (static_branch_unlikely(&ext_gov_load)) {
+		tuner->ext_gov_stat = CPUFREQ_EXT_LOADED;
+		goto unlock;
+	}
 
 	/* As default, set ext_ops_global to nop function */
 	ext_ops_global.get_next_freq = get_next_freq_nop;
@@ -480,6 +492,8 @@ static int ext_gov_init(struct dbs_data *dbs_data)
 	ext_ops_global.init = init_nop;
 	ext_ops_global.exit = exit_nop;
 	strscpy(ext_ops_global.name, CPUFREQ_EXT_DEFAULT_NAME, strlen(CPUFREQ_EXT_DEFAULT_NAME));
+unlock:
+	mutex_unlock(&ext_mutex);
 	return 0;
 }
 
@@ -490,11 +504,14 @@ static void ext_gov_exit(struct dbs_data *dbs_data)
 	if (!dbs_data || !dbs_data->tuners)
 		return;
 
+	mutex_lock(&ext_mutex);
 	tuner = (struct ext_tuner *)dbs_data->tuners;
 	tuner->ext_gov_stat = CPUFREQ_EXT_NONE;
+	if (ext_global_tuner == tuner)
+		ext_global_tuner = NULL;
 	kfree(dbs_data->tuners);
 	dbs_data->tuners = NULL;
-	ext_global_tuner = NULL;
+	mutex_unlock(&ext_mutex);
 }
 
 static void ext_gov_start(struct cpufreq_policy *policy)
-- 
2.43.0


  reply	other threads:[~2026-10-23  8:31 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-09-27 10:13 [RFC PATCH 0/2] cpufreq_ext: Introduce cpufreq ext governor Yipeng Zou
2024-09-27 10:13 ` [RFC PATCH 1/2] " Yipeng Zou
2026-10-23  8:31   ` Hungyu Lin [this message]
2026-10-23 16:04     ` [RFC PATCH] cpufreq: ext: serialize BPF registration with governor teardown Alexei Starovoitov
2026-10-23 16:17       ` Hungyu Lin
2026-10-23 16:23         ` Alexei Starovoitov
2024-09-27 10:13 ` [RFC PATCH 2/2] cpufreq_ext: Add bpf sample Yipeng Zou
2024-09-29 16:56 ` [RFC PATCH 0/2] cpufreq_ext: Introduce cpufreq ext governor Alexei Starovoitov
2024-09-30 18:22   ` Tejun Heo
2024-09-29 23:23 ` Daniel Hodges
2025-09-18 12:52 ` [PATCH] cpufreq: ext: fix NULL deref in ext_gov_update() Jinghao Zhou

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261023083131.58291-1-dennylin0707@gmail.com \
    --to=dennylin0707@gmail.com \
    --cc=bpf@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=rafael@kernel.org \
    --cc=viresh.kumar@linaro.org \
    --cc=zhoujinghao24b@gmail.com \
    --cc=zouyipeng@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox