[PATCH v2] cpufreq: queue policy->update work to a dedicated thread

Gaowei Pu posted 1 patch 2 months, 3 weeks ago
drivers/cpufreq/cpufreq.c | 23 +++++++++++++++++------
include/linux/cpufreq.h   |  4 +++-
2 files changed, 20 insertions(+), 7 deletions(-)
[PATCH v2] cpufreq: queue policy->update work to a dedicated thread
Posted by Gaowei Pu 2 months, 3 weeks ago
We should ensure the low schedule latency of cpu frequency limits work
to meet performance and power demands. so queue the policy->update work
to a dedicated thread.

Remove the rt setting of the thread in patch v1 at Tim and
Rafael J's request. However, it's will not meet everyone's request
when we add a dedicated highpri workqueue to do the policy update work.
Therefore, we keep the thread and will add a vendor hook in andorid aosp
branch lately so we can customize the thread conveniently.

Changes in v2:
- Remove the rt setting of the thread and rephrase commit msg.
- Link to v1: https://lore.kernel.org/lkml/20240717063321.629-1-pugaowei@oppo.com/

Signed-off-by: Gaowei Pu <pugaowei@oppo.com>
---
 drivers/cpufreq/cpufreq.c | 23 +++++++++++++++++------
 include/linux/cpufreq.h   |  4 +++-
 2 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index d7426e1d8bdd..3980f6789a17 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1196,7 +1196,7 @@ void refresh_frequency_limits(struct cpufreq_policy *policy)
 }
 EXPORT_SYMBOL(refresh_frequency_limits);
 
-static void handle_update(struct work_struct *work)
+static void handle_update(struct kthread_work *work)
 {
 	struct cpufreq_policy *policy =
 		container_of(work, struct cpufreq_policy, update);
@@ -1213,7 +1213,7 @@ static int cpufreq_notifier_min(struct notifier_block *nb, unsigned long freq,
 {
 	struct cpufreq_policy *policy = container_of(nb, struct cpufreq_policy, nb_min);
 
-	schedule_work(&policy->update);
+	kthread_queue_work(policy->worker, &policy->update);
 	return 0;
 }
 
@@ -1222,7 +1222,7 @@ static int cpufreq_notifier_max(struct notifier_block *nb, unsigned long freq,
 {
 	struct cpufreq_policy *policy = container_of(nb, struct cpufreq_policy, nb_max);
 
-	schedule_work(&policy->update);
+	kthread_queue_work(policy->worker, &policy->update);
 	return 0;
 }
 
@@ -1305,14 +1305,23 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
 		goto err_min_qos_notifier;
 	}
 
+	policy->worker = kthread_create_worker_on_cpu(cpu, 0, "policy_worker%d", cpu);
+	if (IS_ERR(policy->worker)) {
+		dev_err(dev, "Failed to create policy_worker%d\n", cpu);
+		goto err_max_qos_notifier;
+	}
+
 	INIT_LIST_HEAD(&policy->policy_list);
 	init_rwsem(&policy->rwsem);
 	spin_lock_init(&policy->transition_lock);
 	init_waitqueue_head(&policy->transition_wait);
-	INIT_WORK(&policy->update, handle_update);
+	kthread_init_work(&policy->update, handle_update);
 
 	return policy;
 
+err_max_qos_notifier:
+	freq_qos_remove_notifier(&policy->constraints, FREQ_QOS_MAX,
+				 &policy->nb_max);
 err_min_qos_notifier:
 	freq_qos_remove_notifier(&policy->constraints, FREQ_QOS_MIN,
 				 &policy->nb_min);
@@ -1356,7 +1365,9 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
 				 &policy->nb_min);
 
 	/* Cancel any pending policy->update work before freeing the policy. */
-	cancel_work_sync(&policy->update);
+	kthread_cancel_work_sync(&policy->update);
+	if (policy->worker)
+		kthread_destroy_worker(policy->worker);
 
 	if (policy->max_freq_req) {
 		/*
@@ -1827,7 +1838,7 @@ static unsigned int cpufreq_verify_current_freq(struct cpufreq_policy *policy, b
 
 		cpufreq_out_of_sync(policy, new_freq);
 		if (update)
-			schedule_work(&policy->update);
+			kthread_queue_work(policy->worker, &policy->update);
 	}
 
 	return new_freq;
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 95f3807c8c55..728fa00a9647 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -20,6 +20,7 @@
 #include <linux/spinlock.h>
 #include <linux/sysfs.h>
 #include <linux/minmax.h>
+#include <linux/kthread.h>
 
 /*********************************************************************
  *                        CPUFREQ INTERFACE                          *
@@ -77,8 +78,9 @@ struct cpufreq_policy {
 	void			*governor_data;
 	char			last_governor[CPUFREQ_NAME_LEN]; /* last governor used */
 
-	struct work_struct	update; /* if update_policy() needs to be
+	struct kthread_work	update; /* if update_policy() needs to be
 					 * called, but you're in IRQ context */
+	struct kthread_worker *worker;
 
 	struct freq_constraints	constraints;
 	struct freq_qos_request	*min_freq_req;
-- 
2.17.1
Re: [PATCH v2] cpufreq: queue policy->update work to a dedicated thread
Posted by kernel test robot 2 months, 2 weeks ago
Hi Gaowei,

kernel test robot noticed the following build errors:

[auto build test ERROR on v6.16-rc6]
[also build test ERROR on linus/master next-20250717]
[cannot apply to rafael-pm/linux-next rafael-pm/bleeding-edge]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Gaowei-Pu/cpufreq-queue-policy-update-work-to-a-dedicated-thread/20250717-165319
base:   v6.16-rc6
patch link:    https://lore.kernel.org/r/20250717085110.1468-1-pugaowei%40oppo.com
patch subject: [PATCH v2] cpufreq: queue policy->update work to a dedicated thread
config: i386-buildonly-randconfig-003-20250718 (https://download.01.org/0day-ci/archive/20250718/202507180955.YXkJg7bR-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250718/202507180955.YXkJg7bR-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507180955.YXkJg7bR-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/cpufreq/cpufreq.c: In function 'cpufreq_policy_alloc':
>> drivers/cpufreq/cpufreq.c:1308:26: error: too many arguments to function 'kthread_create_worker_on_cpu'
    1308 |         policy->worker = kthread_create_worker_on_cpu(cpu, 0, "policy_worker%d", cpu);
         |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/cpufreq.h:23,
                    from drivers/cpufreq/cpufreq.c:18:
   include/linux/kthread.h:216:1: note: declared here
     216 | kthread_create_worker_on_cpu(int cpu, unsigned int flags,
         | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~


vim +/kthread_create_worker_on_cpu +1308 drivers/cpufreq/cpufreq.c

  1250	
  1251	static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
  1252	{
  1253		struct cpufreq_policy *policy;
  1254		struct device *dev = get_cpu_device(cpu);
  1255		int ret;
  1256	
  1257		if (!dev)
  1258			return NULL;
  1259	
  1260		policy = kzalloc(sizeof(*policy), GFP_KERNEL);
  1261		if (!policy)
  1262			return NULL;
  1263	
  1264		if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL))
  1265			goto err_free_policy;
  1266	
  1267		if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL))
  1268			goto err_free_cpumask;
  1269	
  1270		if (!zalloc_cpumask_var(&policy->real_cpus, GFP_KERNEL))
  1271			goto err_free_rcpumask;
  1272	
  1273		init_completion(&policy->kobj_unregister);
  1274		ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq,
  1275					   cpufreq_global_kobject, "policy%u", cpu);
  1276		if (ret) {
  1277			dev_err(dev, "%s: failed to init policy->kobj: %d\n", __func__, ret);
  1278			/*
  1279			 * The entire policy object will be freed below, but the extra
  1280			 * memory allocated for the kobject name needs to be freed by
  1281			 * releasing the kobject.
  1282			 */
  1283			kobject_put(&policy->kobj);
  1284			goto err_free_real_cpus;
  1285		}
  1286	
  1287		freq_constraints_init(&policy->constraints);
  1288	
  1289		policy->nb_min.notifier_call = cpufreq_notifier_min;
  1290		policy->nb_max.notifier_call = cpufreq_notifier_max;
  1291	
  1292		ret = freq_qos_add_notifier(&policy->constraints, FREQ_QOS_MIN,
  1293					    &policy->nb_min);
  1294		if (ret) {
  1295			dev_err(dev, "Failed to register MIN QoS notifier: %d (CPU%u)\n",
  1296				ret, cpu);
  1297			goto err_kobj_remove;
  1298		}
  1299	
  1300		ret = freq_qos_add_notifier(&policy->constraints, FREQ_QOS_MAX,
  1301					    &policy->nb_max);
  1302		if (ret) {
  1303			dev_err(dev, "Failed to register MAX QoS notifier: %d (CPU%u)\n",
  1304				ret, cpu);
  1305			goto err_min_qos_notifier;
  1306		}
  1307	
> 1308		policy->worker = kthread_create_worker_on_cpu(cpu, 0, "policy_worker%d", cpu);
  1309		if (IS_ERR(policy->worker)) {
  1310			dev_err(dev, "Failed to create policy_worker%d\n", cpu);
  1311			goto err_max_qos_notifier;
  1312		}
  1313	
  1314		INIT_LIST_HEAD(&policy->policy_list);
  1315		init_rwsem(&policy->rwsem);
  1316		spin_lock_init(&policy->transition_lock);
  1317		init_waitqueue_head(&policy->transition_wait);
  1318		kthread_init_work(&policy->update, handle_update);
  1319	
  1320		return policy;
  1321	
  1322	err_max_qos_notifier:
  1323		freq_qos_remove_notifier(&policy->constraints, FREQ_QOS_MAX,
  1324					 &policy->nb_max);
  1325	err_min_qos_notifier:
  1326		freq_qos_remove_notifier(&policy->constraints, FREQ_QOS_MIN,
  1327					 &policy->nb_min);
  1328	err_kobj_remove:
  1329		cpufreq_policy_put_kobj(policy);
  1330	err_free_real_cpus:
  1331		free_cpumask_var(policy->real_cpus);
  1332	err_free_rcpumask:
  1333		free_cpumask_var(policy->related_cpus);
  1334	err_free_cpumask:
  1335		free_cpumask_var(policy->cpus);
  1336	err_free_policy:
  1337		kfree(policy);
  1338	
  1339		return NULL;
  1340	}
  1341	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v2] cpufreq: queue policy->update work to a dedicated thread
Posted by Rafael J. Wysocki 2 months, 3 weeks ago
On Thu, Jul 17, 2025 at 10:51 AM Gaowei Pu <pugaowei@oppo.com> wrote:
>
> We should ensure the low schedule latency of cpu frequency limits work
> to meet performance and power demands.

Why is the current arrangement insufficient?

> so queue the policy->update work to a dedicated thread.
>
> Remove the rt setting of the thread in patch v1 at Tim and
> Rafael J's request. However, it's will not meet everyone's request
> when we add a dedicated highpri workqueue to do the policy update work.

Why is it insufficient?

> Therefore, we keep the thread and will add a vendor hook in andorid aosp
> branch lately so we can customize the thread conveniently.

If you want to do something in the mainline kernel just for the
convenience of Android AOSP, with all due respect thereof, don't do
it.

This is not going to be considered for 6.17, so you may as well come
back with it when 6.17-rc1 is out.

Thanks!
Re: [PATCH v2] cpufreq: queue policy->update work to a dedicated thread
Posted by Gaowei Pu 2 months, 2 weeks ago
Hi Rafael J,

On 2025/7/18 2:13, Rafael J. Wysocki wrote:
> On Thu, Jul 17, 2025 at 10:51 AM Gaowei Pu <pugaowei@oppo.com> wrote:
>>
>> We should ensure the low schedule latency of cpu frequency limits work
>> to meet performance and power demands.
> 
> Why is the current arrangement insufficient?
> 
>> so queue the policy->update work to a dedicated thread.
>>
>> Remove the rt setting of the thread in patch v1 at Tim and
>> Rafael J's request. However, it's will not meet everyone's request
>> when we add a dedicated highpri workqueue to do the policy update work.
> 
> Why is it insufficient?

Sorry, mabye i didn't make it clear. There are lots of critical tasks with higher
schedule priority than normal cfs tasks because they are related to drawing in android
systems(e.g., mvp tasks on Qualcomm, vip tasks on Mediatek).

The problem we met is that these critical tasks preempt the kworker doing the 'cpufreq policy->update' work
and delay the cpufreq update work for about 10ms. We can't boost the kworker beacause it's a common worker
thread which also doing other works. Therefore, queue the 'cpufreq policy->update' work to a dedicated thread
and customize the thread is a proper way we can do, not just for convenient.

> 
>> Therefore, we keep the thread and will add a vendor hook in andorid aosp
>> branch lately so we can customize the thread conveniently.
> 
> If you want to do something in the mainline kernel just for the
> convenience of Android AOSP, with all due respect thereof, don't do
> it.

Okay, Should i try to pick this patch to Android AOSP branch? thanks.

> 
> This is not going to be considered for 6.17, so you may as well come
> back with it when 6.17-rc1 is out.
> 
> Thanks!
Re: [PATCH v2] cpufreq: queue policy->update work to a dedicated thread
Posted by Markus Elfring 2 months, 3 weeks ago
> when we add a dedicated highpri workqueue to do the policy update work.

                          high-priority?


> Therefore, we keep the thread and will add a vendor hook in andorid aosp

                                                              Android OSP?


> branch lately so we can customize the thread conveniently.
> 
> Changes in v2:
…

Please move your patch version descriptions behind a marker line.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/submitting-patches.rst?h=v6.16-rc6#n784

Regards,
Markus