cpufreq: make sure frequency transitions are serialized
Whenever we are changing frequency of a cpu, we are calling PRECHANGE and POSTCHANGE notifiers. They must be serialized. i.e. PRECHANGE or POSTCHANGE shouldn't be called twice contiguously. This can happen due to bugs in users of __cpufreq_driver_target() or actual cpufreq drivers who are sending these notifiers. This patch adds some protection against this. Now, we keep track of the last transaction and see if something went wrong. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
		
							parent
							
								
									e11158c0c9
								
							
						
					
					
						commit
						7c30ed532c
					
				| @ -312,6 +312,12 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, | |||||||
| 	switch (state) { | 	switch (state) { | ||||||
| 
 | 
 | ||||||
| 	case CPUFREQ_PRECHANGE: | 	case CPUFREQ_PRECHANGE: | ||||||
|  | 		if (WARN(policy->transition_ongoing, | ||||||
|  | 				"In middle of another frequency transition\n")) | ||||||
|  | 			return; | ||||||
|  | 
 | ||||||
|  | 		policy->transition_ongoing = true; | ||||||
|  | 
 | ||||||
| 		/* detect if the driver reported a value as "old frequency"
 | 		/* detect if the driver reported a value as "old frequency"
 | ||||||
| 		 * which is not equal to what the cpufreq core thinks is | 		 * which is not equal to what the cpufreq core thinks is | ||||||
| 		 * "old frequency". | 		 * "old frequency". | ||||||
| @ -331,6 +337,12 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, | |||||||
| 		break; | 		break; | ||||||
| 
 | 
 | ||||||
| 	case CPUFREQ_POSTCHANGE: | 	case CPUFREQ_POSTCHANGE: | ||||||
|  | 		if (WARN(!policy->transition_ongoing, | ||||||
|  | 				"No frequency transition in progress\n")) | ||||||
|  | 			return; | ||||||
|  | 
 | ||||||
|  | 		policy->transition_ongoing = false; | ||||||
|  | 
 | ||||||
| 		adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); | 		adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); | ||||||
| 		pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new, | 		pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new, | ||||||
| 			(unsigned long)freqs->cpu); | 			(unsigned long)freqs->cpu); | ||||||
| @ -1539,6 +1551,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, | |||||||
| 
 | 
 | ||||||
| 	if (cpufreq_disabled()) | 	if (cpufreq_disabled()) | ||||||
| 		return -ENODEV; | 		return -ENODEV; | ||||||
|  | 	if (policy->transition_ongoing) | ||||||
|  | 		return -EBUSY; | ||||||
| 
 | 
 | ||||||
| 	/* Make sure that target_freq is within supported range */ | 	/* Make sure that target_freq is within supported range */ | ||||||
| 	if (target_freq > policy->max) | 	if (target_freq > policy->max) | ||||||
|  | |||||||
| @ -119,6 +119,7 @@ struct cpufreq_policy { | |||||||
| 
 | 
 | ||||||
| 	struct kobject		kobj; | 	struct kobject		kobj; | ||||||
| 	struct completion	kobj_unregister; | 	struct completion	kobj_unregister; | ||||||
|  | 	bool			transition_ongoing; /* Tracks transition status */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define CPUFREQ_ADJUST			(0) | #define CPUFREQ_ADJUST			(0) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user