forked from mirrors/linux
		
	PM / devfreq: Fix devfreq_add_device() when drivers are built as modules.
When the devfreq driver and the governor driver are built as modules,
the call to devfreq_add_device() or governor_store() fails because the
governor driver is not loaded at the time the devfreq driver loads. The
devfreq driver has a build dependency on the governor but also should
have a runtime dependency. We need to make sure that the governor driver
is loaded before the devfreq driver.
This patch fixes this bug by adding a try_then_request_governor()
function. First tries to find the governor, and then, if it is not found,
it requests the module and tries again.
Fixes: 1b5c1be2c8 (PM / devfreq: map devfreq drivers to governor using name)
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
			
			
This commit is contained in:
		
							parent
							
								
									17b57b1883
								
							
						
					
					
						commit
						23c7b54ca1
					
				
					 1 changed files with 49 additions and 4 deletions
				
			
		|  | @ -11,6 +11,7 @@ | |||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/kmod.h> | ||||
| #include <linux/sched.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/err.h> | ||||
|  | @ -221,6 +222,49 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) | |||
| 	return ERR_PTR(-ENODEV); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * try_then_request_governor() - Try to find the governor and request the | ||||
|  *                               module if is not found. | ||||
|  * @name:	name of the governor | ||||
|  * | ||||
|  * Search the list of devfreq governors and request the module and try again | ||||
|  * if is not found. This can happen when both drivers (the governor driver | ||||
|  * and the driver that call devfreq_add_device) are built as modules. | ||||
|  * devfreq_list_lock should be held by the caller. Returns the matched | ||||
|  * governor's pointer. | ||||
|  */ | ||||
| static struct devfreq_governor *try_then_request_governor(const char *name) | ||||
| { | ||||
| 	struct devfreq_governor *governor; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	if (IS_ERR_OR_NULL(name)) { | ||||
| 		pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 	} | ||||
| 	WARN(!mutex_is_locked(&devfreq_list_lock), | ||||
| 	     "devfreq_list_lock must be locked."); | ||||
| 
 | ||||
| 	governor = find_devfreq_governor(name); | ||||
| 	if (IS_ERR(governor)) { | ||||
| 		mutex_unlock(&devfreq_list_lock); | ||||
| 
 | ||||
| 		if (!strncmp(name, DEVFREQ_GOV_SIMPLE_ONDEMAND, | ||||
| 			     DEVFREQ_NAME_LEN)) | ||||
| 			err = request_module("governor_%s", "simpleondemand"); | ||||
| 		else | ||||
| 			err = request_module("governor_%s", name); | ||||
| 		/* Restore previous state before return */ | ||||
| 		mutex_lock(&devfreq_list_lock); | ||||
| 		if (err) | ||||
| 			return NULL; | ||||
| 
 | ||||
| 		governor = find_devfreq_governor(name); | ||||
| 	} | ||||
| 
 | ||||
| 	return governor; | ||||
| } | ||||
| 
 | ||||
| static int devfreq_notify_transition(struct devfreq *devfreq, | ||||
| 		struct devfreq_freqs *freqs, unsigned int state) | ||||
| { | ||||
|  | @ -646,9 +690,8 @@ struct devfreq *devfreq_add_device(struct device *dev, | |||
| 	mutex_unlock(&devfreq->lock); | ||||
| 
 | ||||
| 	mutex_lock(&devfreq_list_lock); | ||||
| 	list_add(&devfreq->node, &devfreq_list); | ||||
| 
 | ||||
| 	governor = find_devfreq_governor(devfreq->governor_name); | ||||
| 	governor = try_then_request_governor(devfreq->governor_name); | ||||
| 	if (IS_ERR(governor)) { | ||||
| 		dev_err(dev, "%s: Unable to find governor for the device\n", | ||||
| 			__func__); | ||||
|  | @ -664,12 +707,14 @@ struct devfreq *devfreq_add_device(struct device *dev, | |||
| 			__func__); | ||||
| 		goto err_init; | ||||
| 	} | ||||
| 
 | ||||
| 	list_add(&devfreq->node, &devfreq_list); | ||||
| 
 | ||||
| 	mutex_unlock(&devfreq_list_lock); | ||||
| 
 | ||||
| 	return devfreq; | ||||
| 
 | ||||
| err_init: | ||||
| 	list_del(&devfreq->node); | ||||
| 	mutex_unlock(&devfreq_list_lock); | ||||
| 
 | ||||
| 	device_unregister(&devfreq->dev); | ||||
|  | @ -991,7 +1036,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, | |||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	mutex_lock(&devfreq_list_lock); | ||||
| 	governor = find_devfreq_governor(str_governor); | ||||
| 	governor = try_then_request_governor(str_governor); | ||||
| 	if (IS_ERR(governor)) { | ||||
| 		ret = PTR_ERR(governor); | ||||
| 		goto out; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Enric Balletbo i Serra
						Enric Balletbo i Serra