PM / devfreq: Fix race condition between suspend/resume and governor_store
There is a race condition when the event governor_store is being executed from sysfs and the device issues a suspend. The devfreq data structures would become stale when the suspend tries to access them in the middle of the governor_store operation. Fix this issue by taking a lock around suspend and resume operations so that these operations are not concurrent with the other events from sysfs. Also rename the sysfs_lock as event_lock since the same lock is used for non sysfs operations like suspend and resume as well. Change-Id: Ifa0e93915a920cec3e0429966328a1128d61098b Signed-off-by: Rama Aparna Mallavarapu <aparnam@codeaurora.org>
This commit is contained in:
@@ -542,7 +542,7 @@ static void devfreq_dev_release(struct device *dev)
|
|||||||
devfreq->profile->exit(devfreq->dev.parent);
|
devfreq->profile->exit(devfreq->dev.parent);
|
||||||
|
|
||||||
mutex_destroy(&devfreq->lock);
|
mutex_destroy(&devfreq->lock);
|
||||||
mutex_destroy(&devfreq->sysfs_lock);
|
mutex_destroy(&devfreq->event_lock);
|
||||||
kfree(devfreq);
|
kfree(devfreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,7 +585,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutex_init(&devfreq->lock);
|
mutex_init(&devfreq->lock);
|
||||||
mutex_init(&devfreq->sysfs_lock);
|
mutex_init(&devfreq->event_lock);
|
||||||
mutex_lock(&devfreq->lock);
|
mutex_lock(&devfreq->lock);
|
||||||
devfreq->dev.parent = dev;
|
devfreq->dev.parent = dev;
|
||||||
devfreq->dev.class = devfreq_class;
|
devfreq->dev.class = devfreq_class;
|
||||||
@@ -817,14 +817,19 @@ EXPORT_SYMBOL(devm_devfreq_remove_device);
|
|||||||
*/
|
*/
|
||||||
int devfreq_suspend_device(struct devfreq *devfreq)
|
int devfreq_suspend_device(struct devfreq *devfreq)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!devfreq)
|
if (!devfreq)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!devfreq->governor)
|
if (!devfreq->governor)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return devfreq->governor->event_handler(devfreq,
|
mutex_lock(&devfreq->event_lock);
|
||||||
|
ret = devfreq->governor->event_handler(devfreq,
|
||||||
DEVFREQ_GOV_SUSPEND, NULL);
|
DEVFREQ_GOV_SUSPEND, NULL);
|
||||||
|
mutex_unlock(&devfreq->event_lock);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(devfreq_suspend_device);
|
EXPORT_SYMBOL(devfreq_suspend_device);
|
||||||
|
|
||||||
@@ -838,14 +843,18 @@ EXPORT_SYMBOL(devfreq_suspend_device);
|
|||||||
*/
|
*/
|
||||||
int devfreq_resume_device(struct devfreq *devfreq)
|
int devfreq_resume_device(struct devfreq *devfreq)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
if (!devfreq)
|
if (!devfreq)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!devfreq->governor)
|
if (!devfreq->governor)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return devfreq->governor->event_handler(devfreq,
|
mutex_lock(&devfreq->event_lock);
|
||||||
|
ret = devfreq->governor->event_handler(devfreq,
|
||||||
DEVFREQ_GOV_RESUME, NULL);
|
DEVFREQ_GOV_RESUME, NULL);
|
||||||
|
mutex_unlock(&devfreq->event_lock);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(devfreq_resume_device);
|
EXPORT_SYMBOL(devfreq_resume_device);
|
||||||
|
|
||||||
@@ -1005,7 +1014,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&df->sysfs_lock);
|
mutex_lock(&df->event_lock);
|
||||||
if (df->governor) {
|
if (df->governor) {
|
||||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
|
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@@ -1031,7 +1040,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
gov_stop_out:
|
gov_stop_out:
|
||||||
mutex_unlock(&df->sysfs_lock);
|
mutex_unlock(&df->event_lock);
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&devfreq_list_lock);
|
mutex_unlock(&devfreq_list_lock);
|
||||||
|
|
||||||
@@ -1126,10 +1135,10 @@ static ssize_t polling_interval_store(struct device *dev,
|
|||||||
if (ret != 1)
|
if (ret != 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&df->sysfs_lock);
|
mutex_lock(&df->event_lock);
|
||||||
df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value);
|
df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value);
|
||||||
ret = count;
|
ret = count;
|
||||||
mutex_unlock(&df->sysfs_lock);
|
mutex_unlock(&df->event_lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1147,7 +1156,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
|
|||||||
if (ret != 1)
|
if (ret != 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&df->sysfs_lock);
|
mutex_lock(&df->event_lock);
|
||||||
mutex_lock(&df->lock);
|
mutex_lock(&df->lock);
|
||||||
max = df->max_freq;
|
max = df->max_freq;
|
||||||
if (value && max && value > max) {
|
if (value && max && value > max) {
|
||||||
@@ -1160,7 +1169,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
|
|||||||
ret = count;
|
ret = count;
|
||||||
unlock:
|
unlock:
|
||||||
mutex_unlock(&df->lock);
|
mutex_unlock(&df->lock);
|
||||||
mutex_unlock(&df->sysfs_lock);
|
mutex_unlock(&df->event_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1184,7 +1193,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
|
|||||||
if (ret != 1)
|
if (ret != 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&df->sysfs_lock);
|
mutex_lock(&df->event_lock);
|
||||||
mutex_lock(&df->lock);
|
mutex_lock(&df->lock);
|
||||||
min = df->min_freq;
|
min = df->min_freq;
|
||||||
if (value && min && value < min) {
|
if (value && min && value < min) {
|
||||||
@@ -1197,7 +1206,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
|
|||||||
ret = count;
|
ret = count;
|
||||||
unlock:
|
unlock:
|
||||||
mutex_unlock(&df->lock);
|
mutex_unlock(&df->lock);
|
||||||
mutex_unlock(&df->sysfs_lock);
|
mutex_unlock(&df->event_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR_RW(min_freq);
|
static DEVICE_ATTR_RW(min_freq);
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ struct devfreq {
|
|||||||
struct list_head node;
|
struct list_head node;
|
||||||
|
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
struct mutex sysfs_lock;
|
struct mutex event_lock;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
struct devfreq_dev_profile *profile;
|
struct devfreq_dev_profile *profile;
|
||||||
const struct devfreq_governor *governor;
|
const struct devfreq_governor *governor;
|
||||||
|
|||||||
Reference in New Issue
Block a user