From 902ad8fa08f2267f83711a9fddf9441b8a02f9dc Mon Sep 17 00:00:00 2001 From: Rama Aparna Mallavarapu Date: Wed, 10 Oct 2018 14:53:32 -0700 Subject: [PATCH] ANDROID: GKI: 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 Bug: 152343889 (cherry picked from commit 8bf200e7136a6896e429e9bfab116238c1d99be6) Signed-off-by: Saravana Kannan --- drivers/devfreq/devfreq.c | 33 +++++++++++++++++++++------------ include/linux/devfreq.h | 2 +- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index c592dab04b34..053b453bcb02 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -582,7 +582,7 @@ static void devfreq_dev_release(struct device *dev) devfreq->profile->exit(devfreq->dev.parent); mutex_destroy(&devfreq->lock); - mutex_destroy(&devfreq->sysfs_lock); + mutex_destroy(&devfreq->event_lock); kfree(devfreq); } @@ -625,7 +625,7 @@ struct devfreq *devfreq_add_device(struct device *dev, } mutex_init(&devfreq->lock); - mutex_init(&devfreq->sysfs_lock); + mutex_init(&devfreq->event_lock); mutex_lock(&devfreq->lock); devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; @@ -862,14 +862,19 @@ EXPORT_SYMBOL(devm_devfreq_remove_device); */ int devfreq_suspend_device(struct devfreq *devfreq) { + int ret; + if (!devfreq) return -EINVAL; if (!devfreq->governor) return 0; - return devfreq->governor->event_handler(devfreq, + mutex_lock(&devfreq->event_lock); + ret = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_SUSPEND, NULL); + mutex_unlock(&devfreq->event_lock); + return ret; } EXPORT_SYMBOL(devfreq_suspend_device); @@ -883,14 +888,18 @@ EXPORT_SYMBOL(devfreq_suspend_device); */ int devfreq_resume_device(struct devfreq *devfreq) { + int ret; if (!devfreq) return -EINVAL; if (!devfreq->governor) return 0; - return devfreq->governor->event_handler(devfreq, + mutex_lock(&devfreq->event_lock); + ret = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_RESUME, NULL); + mutex_unlock(&devfreq->event_lock); + return ret; } EXPORT_SYMBOL(devfreq_resume_device); @@ -1058,7 +1067,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, goto out; } - mutex_lock(&df->sysfs_lock); + mutex_lock(&df->event_lock); if (df->governor) { ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); if (ret) { @@ -1075,7 +1084,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, __func__, df->governor->name, ret); gov_stop_out: - mutex_unlock(&df->sysfs_lock); + mutex_unlock(&df->event_lock); out: mutex_unlock(&devfreq_list_lock); @@ -1170,10 +1179,10 @@ static ssize_t polling_interval_store(struct device *dev, if (ret != 1) return -EINVAL; - mutex_lock(&df->sysfs_lock); + mutex_lock(&df->event_lock); df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value); ret = count; - mutex_unlock(&df->sysfs_lock); + mutex_unlock(&df->event_lock); return ret; } @@ -1190,7 +1199,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; - mutex_lock(&df->sysfs_lock); + mutex_lock(&df->event_lock); mutex_lock(&df->lock); if (value) { @@ -1213,7 +1222,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); - mutex_unlock(&df->sysfs_lock); + mutex_unlock(&df->event_lock); return ret; } @@ -1236,7 +1245,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; - mutex_lock(&df->sysfs_lock); + mutex_lock(&df->event_lock); mutex_lock(&df->lock); if (value) { @@ -1259,7 +1268,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); - mutex_unlock(&df->sysfs_lock); + mutex_unlock(&df->event_lock); return ret; } static DEVICE_ATTR_RW(min_freq); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 45ec9ffb56ba..6ef4fda76d10 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -149,7 +149,7 @@ struct devfreq { struct list_head node; struct mutex lock; - struct mutex sysfs_lock; + struct mutex event_lock; struct device dev; struct devfreq_dev_profile *profile; const struct devfreq_governor *governor;