diff options
Diffstat (limited to 'drivers/base/power/opp/core.c')
-rw-r--r-- | drivers/base/power/opp/core.c | 294 |
1 files changed, 75 insertions, 219 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index 69452a4bcda7..c40d9868bd97 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -32,14 +32,6 @@ LIST_HEAD(opp_tables); /* Lock to allow exclusive modification to the device and opp lists */ DEFINE_MUTEX(opp_table_lock); -#define opp_rcu_lockdep_assert() \ -do { \ - RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ - !lockdep_is_held(&opp_table_lock), \ - "Missing rcu_read_lock() or " \ - "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, @@ -73,8 +65,7 @@ struct opp_table *_find_opp_table_unlocked(struct device *dev) * _find_opp_table() - find opp_table struct using device pointer * @dev: device pointer used to lookup OPP table * - * Search OPP table for one containing matching device. Does a RCU reader - * operation to grab the pointer needed. + * Search OPP table for one containing matching device. * * Return: pointer to 'struct opp_table' if found, otherwise -ENODEV or * -EINVAL based on type of error. @@ -108,19 +99,12 @@ struct opp_table *_find_opp_table(struct device *dev) */ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) { - struct dev_pm_opp *tmp_opp; - unsigned long v = 0; - - rcu_read_lock(); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp)) + if (IS_ERR_OR_NULL(opp)) { pr_err("%s: Invalid parameters\n", __func__); - else - v = tmp_opp->supplies[0].u_volt; + return 0; + } - rcu_read_unlock(); - return v; + return opp->supplies[0].u_volt; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); @@ -133,19 +117,12 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); */ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) { - struct dev_pm_opp *tmp_opp; - unsigned long f = 0; - - rcu_read_lock(); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) + if (IS_ERR_OR_NULL(opp) || !opp->available) { pr_err("%s: Invalid parameters\n", __func__); - else - f = tmp_opp->rate; + return 0; + } - rcu_read_unlock(); - return f; + return opp->rate; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); @@ -161,21 +138,12 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); */ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) { - struct dev_pm_opp *tmp_opp; - bool turbo; - - rcu_read_lock(); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) { + if (IS_ERR_OR_NULL(opp) || !opp->available) { pr_err("%s: Invalid parameters\n", __func__); return false; } - turbo = tmp_opp->turbo; - - rcu_read_unlock(); - return turbo; + return opp->turbo; } EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); @@ -223,8 +191,6 @@ static int _get_regulator_count(struct device *dev) * @dev: device for which we do this operation * * Return: This function returns the max voltage latency in nanoseconds. - * - * Locking: This function takes rcu_read_lock(). */ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) { @@ -256,15 +222,15 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) if (IS_ERR(opp_table)) goto free_uV; - rcu_read_lock(); - memcpy(regulators, opp_table->regulators, count * sizeof(*regulators)); + mutex_lock(&opp_table->lock); + for (i = 0; i < count; i++) { uV[i].min = ~0; uV[i].max = 0; - list_for_each_entry_rcu(opp, &opp_table->opp_list, node) { + list_for_each_entry(opp, &opp_table->opp_list, node) { if (!opp->available) continue; @@ -275,7 +241,7 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) } } - rcu_read_unlock(); + mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table); /* @@ -304,8 +270,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_volt_latency); * * Return: This function returns the max transition latency, in nanoseconds, to * switch from one OPP to other. - * - * Locking: This function takes rcu_read_lock(). */ unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev) { @@ -345,8 +309,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq); * * Return: This function returns the number of available opps if there are any, * else returns 0 if none or the corresponding error value. - * - * Locking: This function takes rcu_read_lock(). */ int dev_pm_opp_get_opp_count(struct device *dev) { @@ -362,14 +324,14 @@ int dev_pm_opp_get_opp_count(struct device *dev) return count; } - rcu_read_lock(); + mutex_lock(&opp_table->lock); - list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available) count++; } - rcu_read_unlock(); + mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table); return count; @@ -414,9 +376,9 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, return ERR_PTR(r); } - rcu_read_lock(); + mutex_lock(&opp_table->lock); - list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available == available && temp_opp->rate == freq) { opp = temp_opp; @@ -427,7 +389,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, } } - rcu_read_unlock(); + mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table); return opp; @@ -439,7 +401,9 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, { struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available && temp_opp->rate >= *freq) { opp = temp_opp; *freq = opp->rate; @@ -450,6 +414,8 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, } } + mutex_unlock(&opp_table->lock); + return opp; } @@ -486,11 +452,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, if (IS_ERR(opp_table)) return ERR_CAST(opp_table); - rcu_read_lock(); - opp = _find_freq_ceil(opp_table, freq); - rcu_read_unlock(); dev_pm_opp_put_opp_table(opp_table); return opp; @@ -530,9 +493,9 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, if (IS_ERR(opp_table)) return ERR_CAST(opp_table); - rcu_read_lock(); + mutex_lock(&opp_table->lock); - list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) { + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available) { /* go to the next node, before choosing prev */ if (temp_opp->rate > *freq) @@ -545,7 +508,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, /* Increment the reference count of OPP */ if (!IS_ERR(opp)) dev_pm_opp_get(opp); - rcu_read_unlock(); + mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table); if (!IS_ERR(opp)) @@ -555,30 +518,6 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); -/* - * The caller needs to ensure that opp_table (and hence the clk) isn't freed, - * while clk returned here is used. - */ -static struct clk *_get_opp_clk(struct device *dev) -{ - struct opp_table *opp_table; - struct clk *clk; - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - dev_err(dev, "%s: device opp doesn't exist\n", __func__); - return ERR_CAST(opp_table); - } - - clk = opp_table->clk; - if (IS_ERR(clk)) - dev_err(dev, "%s: No clock available for the device\n", - __func__); - dev_pm_opp_put_opp_table(opp_table); - - return clk; -} - static int _set_opp_voltage(struct device *dev, struct regulator *reg, struct dev_pm_opp_supply *supply) { @@ -674,8 +613,6 @@ restore_voltage: * * This configures the power-supplies and clock source to the levels specified * by the OPP corresponding to the target_freq. - * - * Locking: This function takes rcu_read_lock(). */ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) { @@ -694,9 +631,19 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) return -EINVAL; } - clk = _get_opp_clk(dev); - if (IS_ERR(clk)) - return PTR_ERR(clk); + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + dev_err(dev, "%s: device opp doesn't exist\n", __func__); + return PTR_ERR(opp_table); + } + + clk = opp_table->clk; + if (IS_ERR(clk)) { + dev_err(dev, "%s: No clock available for the device\n", + __func__); + ret = PTR_ERR(clk); + goto put_opp_table; + } freq = clk_round_rate(clk, target_freq); if ((long)freq <= 0) @@ -708,17 +655,10 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) if (old_freq == freq) { dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do\n", __func__, freq); - return 0; - } - - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) { - dev_err(dev, "%s: device opp doesn't exist\n", __func__); - return PTR_ERR(opp_table); + ret = 0; + goto put_opp_table; } - rcu_read_lock(); - old_opp = _find_freq_ceil(opp_table, &old_freq); if (IS_ERR(old_opp)) { dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n", @@ -730,11 +670,7 @@ 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(); - dev_pm_opp_put_opp_table(opp_table); - return ret; + goto put_old_opp; } dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__, @@ -744,12 +680,8 @@ 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(); - dev_pm_opp_put_opp_table(opp_table); - return _generic_set_opp_clk_only(dev, clk, old_freq, freq); + ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); + goto put_opps; } if (opp_table->set_opp) @@ -773,32 +705,26 @@ 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); + ret = set_opp(data); + +put_opps: dev_pm_opp_put(opp); +put_old_opp: if (!IS_ERR(old_opp)) dev_pm_opp_put(old_opp); - rcu_read_unlock(); +put_opp_table: dev_pm_opp_put_opp_table(opp_table); - - return set_opp(data); + return ret; } EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); /* OPP-dev Helpers */ -static void _kfree_opp_dev_rcu(struct rcu_head *head) -{ - struct opp_device *opp_dev; - - opp_dev = container_of(head, struct opp_device, rcu_head); - kfree_rcu(opp_dev, rcu_head); -} - static void _remove_opp_dev(struct opp_device *opp_dev, struct opp_table *opp_table) { opp_debug_unregister(opp_dev, opp_table); list_del(&opp_dev->node); - call_srcu(&opp_table->srcu_head.srcu, &opp_dev->rcu_head, - _kfree_opp_dev_rcu); + kfree(opp_dev); } struct opp_device *_add_opp_dev(const struct device *dev, @@ -813,7 +739,7 @@ struct opp_device *_add_opp_dev(const struct device *dev, /* Initialize opp-dev */ opp_dev->dev = dev; - list_add_rcu(&opp_dev->node, &opp_table->dev_list); + list_add(&opp_dev->node, &opp_table->dev_list); /* Create debugfs entries for the opp_table */ ret = opp_debug_register(opp_dev, opp_table); @@ -857,28 +783,16 @@ static struct opp_table *_allocate_opp_table(struct device *dev) ret); } - srcu_init_notifier_head(&opp_table->srcu_head); + BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head); INIT_LIST_HEAD(&opp_table->opp_list); mutex_init(&opp_table->lock); kref_init(&opp_table->kref); /* Secure the device table modification */ - list_add_rcu(&opp_table->node, &opp_tables); + list_add(&opp_table->node, &opp_tables); return opp_table; } -/** - * _kfree_device_rcu() - Free opp_table RCU handler - * @head: RCU head - */ -static void _kfree_device_rcu(struct rcu_head *head) -{ - struct opp_table *opp_table = container_of(head, struct opp_table, - rcu_head); - - kfree_rcu(opp_table, rcu_head); -} - void _get_opp_table_kref(struct opp_table *opp_table) { kref_get(&opp_table->kref); @@ -922,9 +836,8 @@ static void _opp_table_kref_release(struct kref *kref) WARN_ON(!list_empty(&opp_table->dev_list)); mutex_destroy(&opp_table->lock); - list_del_rcu(&opp_table->node); - call_srcu(&opp_table->srcu_head.srcu, &opp_table->rcu_head, - _kfree_device_rcu); + list_del(&opp_table->node); + kfree(opp_table); mutex_unlock(&opp_table_lock); } @@ -941,17 +854,6 @@ void _opp_free(struct dev_pm_opp *opp) kfree(opp); } -/** - * _kfree_opp_rcu() - Free OPP RCU handler - * @head: RCU head - */ -static void _kfree_opp_rcu(struct rcu_head *head) -{ - struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head); - - kfree_rcu(opp, rcu_head); -} - static void _opp_kref_release(struct kref *kref) { struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref); @@ -961,10 +863,10 @@ static void _opp_kref_release(struct kref *kref) * Notify the changes in the availability of the operable * frequency/voltage list. */ - srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_REMOVE, opp); + blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_REMOVE, opp); opp_debug_remove_one(opp); - list_del_rcu(&opp->node); - call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); + list_del(&opp->node); + kfree(opp); mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table); @@ -987,12 +889,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put); * @freq: OPP to remove with matching 'freq' * * This function removes an opp from the opp table. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ void dev_pm_opp_remove(struct device *dev, unsigned long freq) { @@ -1097,7 +993,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, mutex_lock(&opp_table->lock); head = &opp_table->opp_list; - list_for_each_entry_rcu(opp, &opp_table->opp_list, node) { + list_for_each_entry(opp, &opp_table->opp_list, node) { if (new_opp->rate > opp->rate) { head = &opp->node; continue; @@ -1120,7 +1016,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, return ret; } - list_add_rcu(&new_opp->node, head); + list_add(&new_opp->node, head); mutex_unlock(&opp_table->lock); new_opp->opp_table = opp_table; @@ -1158,12 +1054,6 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table * and freed by dev_pm_opp_of_remove_table. * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * * Return: * 0 On success OR * Duplicate OPPs (both freq and volt are same) and opp->available @@ -1203,7 +1093,7 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev, * Notify the changes in the availability of the operable * frequency/voltage list. */ - srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp); + blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp); return 0; free_opp: @@ -1580,12 +1470,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper); * The opp is made available by default and it can be controlled using * dev_pm_opp_enable/disable functions. * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * * Return: * 0 On success OR * Duplicate OPPs (both freq and volt are same) and opp->available @@ -1615,18 +1499,12 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add); * @freq: OPP frequency to modify availability * @availability_req: availability status requested for this opp * - * Set the availability of an OPP with an RCU operation, opp_{enable,disable} - * share a common logic which is isolated here. + * Set the availability of an OPP, opp_{enable,disable} share a common logic + * which is isolated here. * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the * copy operation, returns 0 if no modification was done OR modification was * successful. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks to - * keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex locking or synchronize_rcu() blocking calls cannot be used. */ static int _opp_set_availability(struct device *dev, unsigned long freq, bool availability_req) @@ -1672,16 +1550,16 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, /* plug in new node */ new_opp->available = availability_req; - list_replace_rcu(&opp->node, &new_opp->node); - call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); + list_replace(&opp->node, &new_opp->node); + kfree(opp); /* Notify the change of the OPP availability */ if (availability_req) - srcu_notifier_call_chain(&opp_table->srcu_head, - OPP_EVENT_ENABLE, new_opp); + blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ENABLE, + new_opp); else - srcu_notifier_call_chain(&opp_table->srcu_head, - OPP_EVENT_DISABLE, new_opp); + blocking_notifier_call_chain(&opp_table->head, + OPP_EVENT_DISABLE, new_opp); mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table); @@ -1704,12 +1582,6 @@ free_opp: * corresponding error value. It is meant to be used for users an OPP available * after being temporarily made unavailable with dev_pm_opp_disable. * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function indirectly uses RCU and mutex locks to keep the - * integrity of the internal data structures. Callers should ensure that - * this function is *NOT* called under RCU protection or in contexts where - * mutex locking or synchronize_rcu() blocking calls cannot be used. - * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the * copy operation, returns 0 if no modification was done OR modification was * successful. @@ -1730,12 +1602,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_enable); * control by users to make this OPP not available until the circumstances are * right to make it available again (with a call to dev_pm_opp_enable). * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function indirectly uses RCU and mutex locks to keep the - * integrity of the internal data structures. Callers should ensure that - * this function is *NOT* called under RCU protection or in contexts where - * mutex locking or synchronize_rcu() blocking calls cannot be used. - * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the * copy operation, returns 0 if no modification was done OR modification was * successful. @@ -1762,11 +1628,8 @@ int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb) if (IS_ERR(opp_table)) return PTR_ERR(opp_table); - rcu_read_lock(); - - ret = srcu_notifier_chain_register(&opp_table->srcu_head, nb); + ret = blocking_notifier_chain_register(&opp_table->head, nb); - rcu_read_unlock(); dev_pm_opp_put_opp_table(opp_table); return ret; @@ -1790,9 +1653,8 @@ int dev_pm_opp_unregister_notifier(struct device *dev, if (IS_ERR(opp_table)) return PTR_ERR(opp_table); - ret = srcu_notifier_chain_unregister(&opp_table->srcu_head, nb); + ret = blocking_notifier_chain_unregister(&opp_table->head, nb); - rcu_read_unlock(); dev_pm_opp_put_opp_table(opp_table); return ret; @@ -1848,12 +1710,6 @@ void _dev_pm_opp_find_and_remove_table(struct device *dev, bool remove_all) * * Free both OPPs created using static entries present in DT and the * dynamically added entries. - * - * Locking: The internal opp_table and opp structures are RCU protected. - * Hence this function indirectly uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. */ void dev_pm_opp_remove_table(struct device *dev) { |