cpufreq: schedutil: set sg_policy->next_freq to the final cpufreq
Subsystems (2)
Quoted 32 lines · On 27-10-20, 19:54, [email protected] wrote:
Quoted 8 lines
From: zhuguangqing <[email protected]>
In the following code path,
next_freqis clamped betweenpolicy->minandpolicy->maxtwice in functionscpufreq_driver_resolve_freq()andcpufreq_driver_fast_switch(). For there is noupdate_lockin the code path,policy->minandpolicy->maxmay be modified (one or more times), sosg_policy->next_frequpdated in functionsugov_update_next_freq()may be not the final cpufreq.Understood until here, but not sure I followed everything after that.
Quoted 11 lines
Next time when we use
"if (sg_policy->next_freq==next_freq)" to judge whether to updatenext_freq, we may get a wrong result.->
sugov_update_single()
->get_next_freq()-> cpufreq_driver_resolve_freq() -> sugov_fast_switch() -> sugov_update_next_freq() -> cpufreq_driver_fast_switch()Quoted 7 lines
For example, at first
sg_policy->next_freqis 1 GHz, but the final cpufreq is 1.2 GHz becausepolicy->minis modified to 1.2 GHz when we reachedcpufreq_driver_fast_switch(). Then next time,policy->minis changed before we reachedcpufreq_driver_resolve_freq()and (assume)next_freqis 1 GHz, we find "if (sg_policy->next_freq==next_freq)" is satisfied so we don't change the cpufreq. Actually we should change the cpufreq to 1.0 GHz this time.
Quoted 6 lines
FWIW, whenever policy->min/max gets changed,
sg_policy->limits_changedgets set to true bysugov_limits()and the next time schedutil callback gets called from the scheduler, we will fix the frequency.And so there shouldn't be any issue here, unless I am missing something.
Thanks for your comments. Maybe my description was not clear before.
If I understand correctly, when policy->min/max get changed in the time Window between get_next_freq() and sugov_fast_switch(), to be more precise between cpufreq_driver_resolve_freq() and cpufreq_driver_fast_switch(), the issue may happen.
For example, the first time schedutil callback gets called from the scheduler, we reached get_next_freq() and calculate the next_freq, suppose next_freq is 1.0 GHz, then sg_policy->next_freq is updated to 1.0 GHz in sugov_update_next_freq(). If policy->min/max get change right now, suppose policy->min is changed to 1.2 GHz, then the final next_freq is 1.2 GHz for there is another clamp between policy->min and policy->max in cpufreq_driver_fast_switch(). Then sg_policy->next_freq(1.0 GHz) is not the final next_freq(1.2 GHz).
The second time schedutil callback gets called from the scheduler, there are two issues:
(1) Suppose policy->min is still 1.2 GHz, we reached get_next_freq() and calculate the next_freq, because sg_policy->limits_changed gets set to true by sugov_limits() and there is a clamp between policy->min and policy->max, so this time next_freq will be greater than or equal to 1.2 GHz, suppose next_freq is also 1.2 GHz. Now next_freq is 1.2 GHz and sg_policy->next_freq is 1.0 GHz, then we find
"if (sg_policy->next_freq == next_freq)" is not satisfied and we call cpufreq driver to change the cpufreq to 1.2 GHz. Actually it's already 1.2 GHz, it's not necessary to change this time.
(2) Suppose policy->min was changed again to 1.0 GHz before, we reached get_next_freq() and calculate the next_freq, suppose next_freq is also 1.0 GHz. Now next_freq is 1.0 GHz and sg_policy->next_freq is also 1.0 GHz, then we find "if (sg_policy->next_freq == next_freq)" is satisfied and we don't change the cpufreq. Actually we should change the cpufreq to 1.0 GHz this time.
Quoted 3 lines
--
viresh
Viresh Kumar
maintainerQuoted 16 lines · On 28-10-20, 19:03, zhuguangqing83 wrote:
Thanks for your comments. Maybe my description was not clear before.
If I understand correctly, when policy->min/max get changed in the time Window between
get_next_freq()andsugov_fast_switch(), to be more precise betweencpufreq_driver_resolve_freq()andcpufreq_driver_fast_switch(), the issue may happen.For example, the first time schedutil callback gets called from the scheduler, we reached
get_next_freq()and calculate thenext_freq, supposenext_freqis 1.0 GHz, thensg_policy->next_freqis updated to 1.0 GHz insugov_update_next_freq(). If policy->min/max get change right now, supposepolicy->minis changed to 1.2 GHz, then the finalnext_freqis 1.2 GHz for there is another clamp betweenpolicy->minandpolicy->maxincpufreq_driver_fast_switch(). Thensg_policy->next_freq(1.0 GHz)is not the finalnext_freq(1.2 GHz).
Quoted 11 lines
The second time schedutil callback gets called from the scheduler, there are two issues:
(1) Supposepolicy->minis still 1.2 GHz, we reachedget_next_freq()and calculate thenext_freq, becausesg_policy->limits_changedgets set to true bysugov_limits()and there is a clamp betweenpolicy->minandpolicy->max, so this timenext_freqwill be greater than or equal to 1.2 GHz, supposenext_freqis also 1.2 GHz. Nownext_freqis 1.2 GHz andsg_policy->next_freqis 1.0 GHz, then we find
"if (sg_policy->next_freq==next_freq)" is not satisfied and we call cpufreq driver to change the cpufreq to 1.2 GHz. Actually it's already 1.2 GHz, it's not necessary to change this time.
This isn't that bad, but ...
Quoted 6 lines
(2) Suppose
policy->minwas changed again to 1.0 GHz before, we reachedget_next_freq()and calculate thenext_freq, supposenext_freqis also 1.0 GHz. Nownext_freqis 1.0 GHz andsg_policy->next_freqis also 1.0 GHz, then we find "if (sg_policy->next_freq==next_freq)" is satisfied and we don't change the cpufreq. Actually we should change the cpufreq to 1.0 GHz this time.
This is a real problem we can get into. What about this diff instead ?
kernel/sched/cpufreq_schedutil.c +2 -2
@@ -105,7 +105,6 @@ static bool sugov_update_next_freq(struct sugov_policy *sg_policy, u64 time,if (sg_policy->next_freq == next_freq)return false; - sg_policy->next_freq = next_freq; sg_policy->last_freq_update_time = time;return true;
@@ -115,7 +114,7 @@ static void sugov_fast_switch(struct sugov_policy *sg_policy, u64 time, unsigned int next_freq) {if (sugov_update_next_freq (sg_policy, time, next_freq)) -cpufreq_driver_fast_switch (sg_policy->policy, next_freq); + sg_policy->next_freq =cpufreq_driver_fast_switch (sg_policy->policy, next_freq); } static void sugov_deferred_update(struct sugov_policy *sg_policy, u64 time,
@@ -124,6 +123,7 @@ static void sugov_deferred_update(struct sugov_policy *sg_policy, u64 time,if (!sugov_update_next_freq (sg_policy, time, next_freq))return ; + sg_policy->next_freq = next_freq;if (!sg_policy->work_in_progress) { sg_policy->work_in_progress = true;irq_work_queue (&sg_policy->irq_work);
Signature · 1 line
viresh