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:
Rama Aparna Mallavarapu
2018-10-10 14:53:32 -07:00
parent 2e9e15b75b
commit 8bf200e713
2 changed files with 22 additions and 13 deletions

View File

@@ -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);

View File

@@ -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;