diff options
Diffstat (limited to 'kernel/sched/cpufreq_schedutil.c')
| -rw-r--r-- | kernel/sched/cpufreq_schedutil.c | 49 | 
1 files changed, 42 insertions, 7 deletions
| diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 1a19d69b91ed..816f07f9d30f 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -81,9 +81,23 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)  	if (!cpufreq_this_cpu_can_update(sg_policy->policy))  		return false; -	if (unlikely(sg_policy->limits_changed)) { -		sg_policy->limits_changed = false; -		sg_policy->need_freq_update = cpufreq_driver_test_flags(CPUFREQ_NEED_UPDATE_LIMITS); +	if (unlikely(READ_ONCE(sg_policy->limits_changed))) { +		WRITE_ONCE(sg_policy->limits_changed, false); +		sg_policy->need_freq_update = true; + +		/* +		 * The above limits_changed update must occur before the reads +		 * of policy limits in cpufreq_driver_resolve_freq() or a policy +		 * limits update might be missed, so use a memory barrier to +		 * ensure it. +		 * +		 * This pairs with the write memory barrier in sugov_limits(). +		 */ +		smp_mb(); + +		return true; +	} else if (sg_policy->need_freq_update) { +		/* ignore_dl_rate_limit() wants a new frequency to be found. */  		return true;  	} @@ -95,10 +109,22 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)  static bool sugov_update_next_freq(struct sugov_policy *sg_policy, u64 time,  				   unsigned int next_freq)  { -	if (sg_policy->need_freq_update) +	if (sg_policy->need_freq_update) {  		sg_policy->need_freq_update = false; -	else if (sg_policy->next_freq == next_freq) +		/* +		 * The policy limits have changed, but if the return value of +		 * cpufreq_driver_resolve_freq() after applying the new limits +		 * is still equal to the previously selected frequency, the +		 * driver callback need not be invoked unless the driver +		 * specifically wants that to happen on every update of the +		 * policy limits. +		 */ +		if (sg_policy->next_freq == next_freq && +		    !cpufreq_driver_test_flags(CPUFREQ_NEED_UPDATE_LIMITS)) +			return false; +	} else if (sg_policy->next_freq == next_freq) {  		return false; +	}  	sg_policy->next_freq = next_freq;  	sg_policy->last_freq_update_time = time; @@ -365,7 +391,7 @@ static inline bool sugov_hold_freq(struct sugov_cpu *sg_cpu) { return false; }  static inline void ignore_dl_rate_limit(struct sugov_cpu *sg_cpu)  {  	if (cpu_bw_dl(cpu_rq(sg_cpu->cpu)) > sg_cpu->bw_min) -		sg_cpu->sg_policy->limits_changed = true; +		sg_cpu->sg_policy->need_freq_update = true;  }  static inline bool sugov_update_single_common(struct sugov_cpu *sg_cpu, @@ -871,7 +897,16 @@ static void sugov_limits(struct cpufreq_policy *policy)  		mutex_unlock(&sg_policy->work_lock);  	} -	sg_policy->limits_changed = true; +	/* +	 * The limits_changed update below must take place before the updates +	 * of policy limits in cpufreq_set_policy() or a policy limits update +	 * might be missed, so use a memory barrier to ensure it. +	 * +	 * This pairs with the memory barrier in sugov_should_update_freq(). +	 */ +	smp_wmb(); + +	WRITE_ONCE(sg_policy->limits_changed, true);  }  struct cpufreq_governor schedutil_gov = { | 
