diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2017-01-23 07:41:47 +0300 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-01-30 11:22:21 +0300 |
commit | 8a31d9d94297b1ecae3012069d35d78c959693c2 (patch) | |
tree | 97bc7ee54340bea61c32cc3cf102054ca30f0174 /drivers/base/power | |
parent | 7034764a1e4a6edbb60914e89aad8384e3fe5d17 (diff) | |
download | linux-8a31d9d94297b1ecae3012069d35d78c959693c2.tar.xz |
PM / OPP: Update OPP users to put reference
This patch updates dev_pm_opp_find_freq_*() routines to get a reference
to the OPPs returned by them.
Also updates the users of dev_pm_opp_find_freq_*() routines to call
dev_pm_opp_put() after they are done using the OPPs.
As it is guaranteed the that OPPs wouldn't get freed while being used,
the RCU read side locking present with the users isn't required anymore.
Drop it as well.
This patch also updates all users of devfreq_recommended_opp() which was
returning an OPP received from the OPP core.
Note that some of the OPP core routines have gained
rcu_read_{lock|unlock}() calls, as those still use RCU specific APIs
within them.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com> [Devfreq]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/base/power')
-rw-r--r-- | drivers/base/power/opp/core.c | 114 | ||||
-rw-r--r-- | drivers/base/power/opp/cpu.c | 22 |
2 files changed, 69 insertions, 67 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index 9870ee54d708..a6efa818029a 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -40,6 +40,8 @@ do { \ "opp_table_lock protection"); \ } while (0) +static void dev_pm_opp_get(struct dev_pm_opp *opp); + static struct opp_device *_find_opp_dev(const struct device *dev, struct opp_table *opp_table) { @@ -94,21 +96,13 @@ struct opp_table *_find_opp_table(struct device *dev) * return 0 * * This is useful only for devices with single power supply. - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. This means that opp which could have been fetched by - * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are - * under RCU lock. The pointer returned by the opp_find_freq family must be - * used in the same section as the usage of this function with the pointer - * prior to unlocking with rcu_read_unlock() to maintain the integrity of the - * pointer. */ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) { struct dev_pm_opp *tmp_opp; unsigned long v = 0; - opp_rcu_lockdep_assert(); + rcu_read_lock(); tmp_opp = rcu_dereference(opp); if (IS_ERR_OR_NULL(tmp_opp)) @@ -116,6 +110,7 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) else v = tmp_opp->supplies[0].u_volt; + rcu_read_unlock(); return v; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); @@ -126,21 +121,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); * * Return: frequency in hertz corresponding to the opp, else * return 0 - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. This means that opp which could have been fetched by - * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are - * under RCU lock. The pointer returned by the opp_find_freq family must be - * used in the same section as the usage of this function with the pointer - * prior to unlocking with rcu_read_unlock() to maintain the integrity of the - * pointer. */ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) { struct dev_pm_opp *tmp_opp; unsigned long f = 0; - opp_rcu_lockdep_assert(); + rcu_read_lock(); tmp_opp = rcu_dereference(opp); if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) @@ -148,6 +135,7 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) else f = tmp_opp->rate; + rcu_read_unlock(); return f; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); @@ -161,20 +149,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); * quickly. Running on them for longer times may overheat the chip. * * Return: true if opp is turbo opp, else false. - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. This means that opp which could have been fetched by - * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are - * under RCU lock. The pointer returned by the opp_find_freq family must be - * used in the same section as the usage of this function with the pointer - * prior to unlocking with rcu_read_unlock() to maintain the integrity of the - * pointer. */ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) { struct dev_pm_opp *tmp_opp; + bool turbo; - opp_rcu_lockdep_assert(); + rcu_read_lock(); tmp_opp = rcu_dereference(opp); if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) { @@ -182,7 +163,10 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) return false; } - return tmp_opp->turbo; + turbo = tmp_opp->turbo; + + rcu_read_unlock(); + return turbo; } EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); @@ -410,11 +394,8 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); * This provides a mechanism to enable an opp which is not available currently * or the opposite as well. * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. */ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, @@ -423,13 +404,14 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, struct opp_table *opp_table; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - opp_rcu_lockdep_assert(); + rcu_read_lock(); opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { int r = PTR_ERR(opp_table); dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); + rcu_read_unlock(); return ERR_PTR(r); } @@ -437,10 +419,15 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, if (temp_opp->available == available && temp_opp->rate == freq) { opp = temp_opp; + + /* Increment the reference count of OPP */ + dev_pm_opp_get(opp); break; } } + rcu_read_unlock(); + return opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); @@ -454,6 +441,9 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, if (temp_opp->available && temp_opp->rate >= *freq) { opp = temp_opp; *freq = opp->rate; + + /* Increment the reference count of OPP */ + dev_pm_opp_get(opp); break; } } @@ -476,29 +466,33 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, * ERANGE: no match found for search * ENODEV: if device not found in list of registered devices * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. */ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq) { struct opp_table *opp_table; - - opp_rcu_lockdep_assert(); + struct dev_pm_opp *opp; if (!dev || !freq) { dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); return ERR_PTR(-EINVAL); } + rcu_read_lock(); + opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) + if (IS_ERR(opp_table)) { + rcu_read_unlock(); return ERR_CAST(opp_table); + } + + opp = _find_freq_ceil(opp_table, freq); - return _find_freq_ceil(opp_table, freq); + rcu_read_unlock(); + + return opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); @@ -517,11 +511,8 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); * ERANGE: no match found for search * ENODEV: if device not found in list of registered devices * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. */ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) @@ -529,16 +520,18 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, struct opp_table *opp_table; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - opp_rcu_lockdep_assert(); - if (!dev || !freq) { dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); return ERR_PTR(-EINVAL); } + rcu_read_lock(); + opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) + if (IS_ERR(opp_table)) { + rcu_read_unlock(); return ERR_CAST(opp_table); + } list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available) { @@ -549,6 +542,12 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, opp = temp_opp; } } + + /* Increment the reference count of OPP */ + if (!IS_ERR(opp)) + dev_pm_opp_get(opp); + rcu_read_unlock(); + if (!IS_ERR(opp)) *freq = opp->rate; @@ -736,6 +735,8 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) ret = PTR_ERR(opp); dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n", __func__, freq, ret); + if (!IS_ERR(old_opp)) + dev_pm_opp_put(old_opp); rcu_read_unlock(); return ret; } @@ -747,6 +748,9 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) /* Only frequency scaling */ if (!regulators) { + dev_pm_opp_put(opp); + if (!IS_ERR(old_opp)) + dev_pm_opp_put(old_opp); rcu_read_unlock(); return _generic_set_opp_clk_only(dev, clk, old_freq, freq); } @@ -772,6 +776,9 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) data->new_opp.rate = freq; memcpy(data->new_opp.supplies, opp->supplies, size); + dev_pm_opp_put(opp); + if (!IS_ERR(old_opp)) + dev_pm_opp_put(old_opp); rcu_read_unlock(); return set_opp(data); @@ -967,6 +974,11 @@ static void _opp_kref_release(struct kref *kref) dev_pm_opp_put_opp_table(opp_table); } +static void dev_pm_opp_get(struct dev_pm_opp *opp) +{ + kref_get(&opp->kref); +} + void dev_pm_opp_put(struct dev_pm_opp *opp) { kref_put_mutex(&opp->kref, _opp_kref_release, &opp->opp_table->lock); diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c index 8c3434bdb26d..adef788862d5 100644 --- a/drivers/base/power/opp/cpu.c +++ b/drivers/base/power/opp/cpu.c @@ -42,11 +42,6 @@ * * WARNING: It is important for the callers to ensure refreshing their copy of * the table if any of the mentioned functions have been invoked in the interim. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Since we just use the regular accessor functions to access the internal data - * structures, we use RCU read lock inside this function. As a result, users of - * this function DONOT need to use explicit locks for invoking. */ int dev_pm_opp_init_cpufreq_table(struct device *dev, struct cpufreq_frequency_table **table) @@ -56,19 +51,13 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev, int i, max_opps, ret = 0; unsigned long rate; - rcu_read_lock(); - max_opps = dev_pm_opp_get_opp_count(dev); - if (max_opps <= 0) { - ret = max_opps ? max_opps : -ENODATA; - goto out; - } + if (max_opps <= 0) + return max_opps ? max_opps : -ENODATA; freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_ATOMIC); - if (!freq_table) { - ret = -ENOMEM; - goto out; - } + if (!freq_table) + return -ENOMEM; for (i = 0, rate = 0; i < max_opps; i++, rate++) { /* find next rate */ @@ -83,6 +72,8 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev, /* Is Boost/turbo opp ? */ if (dev_pm_opp_is_turbo(opp)) freq_table[i].flags = CPUFREQ_BOOST_FREQ; + + dev_pm_opp_put(opp); } freq_table[i].driver_data = i; @@ -91,7 +82,6 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev, *table = &freq_table[0]; out: - rcu_read_unlock(); if (ret) kfree(freq_table); |