Skip to content

Commit 0bed612

Browse files
committed
cpufreq: sched: Helpers to add and remove update_util hooks
Replace the single helper for adding and removing cpufreq utilization update hooks, cpufreq_set_update_util_data(), with a pair of helpers, cpufreq_add_update_util_hook() and cpufreq_remove_update_util_hook(), and modify the users of cpufreq_set_update_util_data() accordingly. With the new helpers, the code using them doesn't need to worry about the internals of struct update_util_data and in particular it doesn't need to worry about populating the func field in it properly upfront. Signed-off-by: Rafael J. Wysocki <[email protected]> Acked-by: Viresh Kumar <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]>
1 parent 9fa64d6 commit 0bed612

File tree

4 files changed

+82
-54
lines changed

4 files changed

+82
-54
lines changed

drivers/cpufreq/cpufreq_governor.c

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -258,43 +258,6 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
258258
}
259259
EXPORT_SYMBOL_GPL(dbs_update);
260260

261-
static void gov_set_update_util(struct policy_dbs_info *policy_dbs,
262-
unsigned int delay_us)
263-
{
264-
struct cpufreq_policy *policy = policy_dbs->policy;
265-
int cpu;
266-
267-
gov_update_sample_delay(policy_dbs, delay_us);
268-
policy_dbs->last_sample_time = 0;
269-
270-
for_each_cpu(cpu, policy->cpus) {
271-
struct cpu_dbs_info *cdbs = &per_cpu(cpu_dbs, cpu);
272-
273-
cpufreq_set_update_util_data(cpu, &cdbs->update_util);
274-
}
275-
}
276-
277-
static inline void gov_clear_update_util(struct cpufreq_policy *policy)
278-
{
279-
int i;
280-
281-
for_each_cpu(i, policy->cpus)
282-
cpufreq_set_update_util_data(i, NULL);
283-
284-
synchronize_sched();
285-
}
286-
287-
static void gov_cancel_work(struct cpufreq_policy *policy)
288-
{
289-
struct policy_dbs_info *policy_dbs = policy->governor_data;
290-
291-
gov_clear_update_util(policy_dbs->policy);
292-
irq_work_sync(&policy_dbs->irq_work);
293-
cancel_work_sync(&policy_dbs->work);
294-
atomic_set(&policy_dbs->work_count, 0);
295-
policy_dbs->work_in_progress = false;
296-
}
297-
298261
static void dbs_work_handler(struct work_struct *work)
299262
{
300263
struct policy_dbs_info *policy_dbs;
@@ -382,6 +345,44 @@ static void dbs_update_util_handler(struct update_util_data *data, u64 time,
382345
irq_work_queue(&policy_dbs->irq_work);
383346
}
384347

348+
static void gov_set_update_util(struct policy_dbs_info *policy_dbs,
349+
unsigned int delay_us)
350+
{
351+
struct cpufreq_policy *policy = policy_dbs->policy;
352+
int cpu;
353+
354+
gov_update_sample_delay(policy_dbs, delay_us);
355+
policy_dbs->last_sample_time = 0;
356+
357+
for_each_cpu(cpu, policy->cpus) {
358+
struct cpu_dbs_info *cdbs = &per_cpu(cpu_dbs, cpu);
359+
360+
cpufreq_add_update_util_hook(cpu, &cdbs->update_util,
361+
dbs_update_util_handler);
362+
}
363+
}
364+
365+
static inline void gov_clear_update_util(struct cpufreq_policy *policy)
366+
{
367+
int i;
368+
369+
for_each_cpu(i, policy->cpus)
370+
cpufreq_remove_update_util_hook(i);
371+
372+
synchronize_sched();
373+
}
374+
375+
static void gov_cancel_work(struct cpufreq_policy *policy)
376+
{
377+
struct policy_dbs_info *policy_dbs = policy->governor_data;
378+
379+
gov_clear_update_util(policy_dbs->policy);
380+
irq_work_sync(&policy_dbs->irq_work);
381+
cancel_work_sync(&policy_dbs->work);
382+
atomic_set(&policy_dbs->work_count, 0);
383+
policy_dbs->work_in_progress = false;
384+
}
385+
385386
static struct policy_dbs_info *alloc_policy_dbs_info(struct cpufreq_policy *policy,
386387
struct dbs_governor *gov)
387388
{
@@ -404,7 +405,6 @@ static struct policy_dbs_info *alloc_policy_dbs_info(struct cpufreq_policy *poli
404405
struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j);
405406

406407
j_cdbs->policy_dbs = policy_dbs;
407-
j_cdbs->update_util.func = dbs_update_util_handler;
408408
}
409409
return policy_dbs;
410410
}

drivers/cpufreq/intel_pstate.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,8 +1107,6 @@ static int intel_pstate_init_cpu(unsigned int cpunum)
11071107

11081108
intel_pstate_busy_pid_reset(cpu);
11091109

1110-
cpu->update_util.func = intel_pstate_update_util;
1111-
11121110
pr_debug("intel_pstate: controlling: cpu %d\n", cpunum);
11131111

11141112
return 0;
@@ -1132,12 +1130,13 @@ static void intel_pstate_set_update_util_hook(unsigned int cpu_num)
11321130

11331131
/* Prevent intel_pstate_update_util() from using stale data. */
11341132
cpu->sample.time = 0;
1135-
cpufreq_set_update_util_data(cpu_num, &cpu->update_util);
1133+
cpufreq_add_update_util_hook(cpu_num, &cpu->update_util,
1134+
intel_pstate_update_util);
11361135
}
11371136

11381137
static void intel_pstate_clear_update_util_hook(unsigned int cpu)
11391138
{
1140-
cpufreq_set_update_util_data(cpu, NULL);
1139+
cpufreq_remove_update_util_hook(cpu);
11411140
synchronize_sched();
11421141
}
11431142

include/linux/sched.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3240,7 +3240,10 @@ struct update_util_data {
32403240
u64 time, unsigned long util, unsigned long max);
32413241
};
32423242

3243-
void cpufreq_set_update_util_data(int cpu, struct update_util_data *data);
3243+
void cpufreq_add_update_util_hook(int cpu, struct update_util_data *data,
3244+
void (*func)(struct update_util_data *data, u64 time,
3245+
unsigned long util, unsigned long max));
3246+
void cpufreq_remove_update_util_hook(int cpu);
32443247
#endif /* CONFIG_CPU_FREQ */
32453248

32463249
#endif

kernel/sched/cpufreq.c

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,50 @@
1414
DEFINE_PER_CPU(struct update_util_data *, cpufreq_update_util_data);
1515

1616
/**
17-
* cpufreq_set_update_util_data - Populate the CPU's update_util_data pointer.
17+
* cpufreq_add_update_util_hook - Populate the CPU's update_util_data pointer.
1818
* @cpu: The CPU to set the pointer for.
1919
* @data: New pointer value.
20+
* @func: Callback function to set for the CPU.
2021
*
21-
* Set and publish the update_util_data pointer for the given CPU. That pointer
22-
* points to a struct update_util_data object containing a callback function
23-
* to call from cpufreq_update_util(). That function will be called from an RCU
24-
* read-side critical section, so it must not sleep.
22+
* Set and publish the update_util_data pointer for the given CPU.
2523
*
26-
* Callers must use RCU-sched callbacks to free any memory that might be
27-
* accessed via the old update_util_data pointer or invoke synchronize_sched()
28-
* right after this function to avoid use-after-free.
24+
* The update_util_data pointer of @cpu is set to @data and the callback
25+
* function pointer in the target struct update_util_data is set to @func.
26+
* That function will be called by cpufreq_update_util() from RCU-sched
27+
* read-side critical sections, so it must not sleep. @data will always be
28+
* passed to it as the first argument which allows the function to get to the
29+
* target update_util_data structure and its container.
30+
*
31+
* The update_util_data pointer of @cpu must be NULL when this function is
32+
* called or it will WARN() and return with no effect.
2933
*/
30-
void cpufreq_set_update_util_data(int cpu, struct update_util_data *data)
34+
void cpufreq_add_update_util_hook(int cpu, struct update_util_data *data,
35+
void (*func)(struct update_util_data *data, u64 time,
36+
unsigned long util, unsigned long max))
3137
{
32-
if (WARN_ON(data && !data->func))
38+
if (WARN_ON(!data || !func))
3339
return;
3440

41+
if (WARN_ON(per_cpu(cpufreq_update_util_data, cpu)))
42+
return;
43+
44+
data->func = func;
3545
rcu_assign_pointer(per_cpu(cpufreq_update_util_data, cpu), data);
3646
}
37-
EXPORT_SYMBOL_GPL(cpufreq_set_update_util_data);
47+
EXPORT_SYMBOL_GPL(cpufreq_add_update_util_hook);
48+
49+
/**
50+
* cpufreq_remove_update_util_hook - Clear the CPU's update_util_data pointer.
51+
* @cpu: The CPU to clear the pointer for.
52+
*
53+
* Clear the update_util_data pointer for the given CPU.
54+
*
55+
* Callers must use RCU-sched callbacks to free any memory that might be
56+
* accessed via the old update_util_data pointer or invoke synchronize_sched()
57+
* right after this function to avoid use-after-free.
58+
*/
59+
void cpufreq_remove_update_util_hook(int cpu)
60+
{
61+
rcu_assign_pointer(per_cpu(cpufreq_update_util_data, cpu), NULL);
62+
}
63+
EXPORT_SYMBOL_GPL(cpufreq_remove_update_util_hook);

0 commit comments

Comments
 (0)