aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2017-01-22 23:41:41 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-01-30 03:22:20 -0500
commit37a73ec0c9bbd712d24cc2035696893e5b6119a5 (patch)
treef26471abf34316fc58ea9fbb19e5eaba2f4f0d78
parentb6160e26936bcf1b9181bb34ad4f420ccd3f39f0 (diff)
PM / OPP: Add per OPP table mutex
Add per OPP table lock to protect opp_table->opp_list. Note that at few places opp_list is used under the rcu_read_lock() and so a mutex can't be added there for now. This will be fixed by a later patch. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/base/power/opp/core.c31
-rw-r--r--drivers/base/power/opp/opp.h2
2 files changed, 29 insertions, 4 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index 622dd32f8dda..dcebd5efb6a1 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -854,6 +854,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev)
854 854
855 srcu_init_notifier_head(&opp_table->srcu_head); 855 srcu_init_notifier_head(&opp_table->srcu_head);
856 INIT_LIST_HEAD(&opp_table->opp_list); 856 INIT_LIST_HEAD(&opp_table->opp_list);
857 mutex_init(&opp_table->lock);
857 858
858 /* Secure the device table modification */ 859 /* Secure the device table modification */
859 list_add_rcu(&opp_table->node, &opp_tables); 860 list_add_rcu(&opp_table->node, &opp_tables);
@@ -909,6 +910,7 @@ static void _free_opp_table(struct opp_table *opp_table)
909 /* dev_list must be empty now */ 910 /* dev_list must be empty now */
910 WARN_ON(!list_empty(&opp_table->dev_list)); 911 WARN_ON(!list_empty(&opp_table->dev_list));
911 912
913 mutex_destroy(&opp_table->lock);
912 list_del_rcu(&opp_table->node); 914 list_del_rcu(&opp_table->node);
913 call_srcu(&opp_table->srcu_head.srcu, &opp_table->rcu_head, 915 call_srcu(&opp_table->srcu_head.srcu, &opp_table->rcu_head,
914 _kfree_device_rcu); 916 _kfree_device_rcu);
@@ -969,6 +971,8 @@ static void _kfree_opp_rcu(struct rcu_head *head)
969 */ 971 */
970static void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp) 972static void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp)
971{ 973{
974 mutex_lock(&opp_table->lock);
975
972 /* 976 /*
973 * Notify the changes in the availability of the operable 977 * Notify the changes in the availability of the operable
974 * frequency/voltage list. 978 * frequency/voltage list.
@@ -978,6 +982,8 @@ static void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp)
978 list_del_rcu(&opp->node); 982 list_del_rcu(&opp->node);
979 call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); 983 call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
980 984
985 mutex_unlock(&opp_table->lock);
986
981 _remove_opp_table(opp_table); 987 _remove_opp_table(opp_table);
982} 988}
983 989
@@ -1007,6 +1013,8 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)
1007 if (IS_ERR(opp_table)) 1013 if (IS_ERR(opp_table))
1008 goto unlock; 1014 goto unlock;
1009 1015
1016 mutex_lock(&opp_table->lock);
1017
1010 list_for_each_entry(opp, &opp_table->opp_list, node) { 1018 list_for_each_entry(opp, &opp_table->opp_list, node) {
1011 if (opp->rate == freq) { 1019 if (opp->rate == freq) {
1012 found = true; 1020 found = true;
@@ -1014,6 +1022,8 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)
1014 } 1022 }
1015 } 1023 }
1016 1024
1025 mutex_unlock(&opp_table->lock);
1026
1017 if (!found) { 1027 if (!found) {
1018 dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n", 1028 dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n",
1019 __func__, freq); 1029 __func__, freq);
@@ -1083,7 +1093,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
1083 struct opp_table *opp_table) 1093 struct opp_table *opp_table)
1084{ 1094{
1085 struct dev_pm_opp *opp; 1095 struct dev_pm_opp *opp;
1086 struct list_head *head = &opp_table->opp_list; 1096 struct list_head *head;
1087 int ret; 1097 int ret;
1088 1098
1089 /* 1099 /*
@@ -1094,6 +1104,9 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
1094 * loop, don't replace it with head otherwise it will become an infinite 1104 * loop, don't replace it with head otherwise it will become an infinite
1095 * loop. 1105 * loop.
1096 */ 1106 */
1107 mutex_lock(&opp_table->lock);
1108 head = &opp_table->opp_list;
1109
1097 list_for_each_entry_rcu(opp, &opp_table->opp_list, node) { 1110 list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
1098 if (new_opp->rate > opp->rate) { 1111 if (new_opp->rate > opp->rate) {
1099 head = &opp->node; 1112 head = &opp->node;
@@ -1110,12 +1123,17 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
1110 new_opp->supplies[0].u_volt, new_opp->available); 1123 new_opp->supplies[0].u_volt, new_opp->available);
1111 1124
1112 /* Should we compare voltages for all regulators here ? */ 1125 /* Should we compare voltages for all regulators here ? */
1113 return opp->available && 1126 ret = opp->available &&
1114 new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? -EBUSY : -EEXIST; 1127 new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? -EBUSY : -EEXIST;
1128
1129 mutex_unlock(&opp_table->lock);
1130 return ret;
1115 } 1131 }
1116 1132
1117 new_opp->opp_table = opp_table;
1118 list_add_rcu(&new_opp->node, head); 1133 list_add_rcu(&new_opp->node, head);
1134 mutex_unlock(&opp_table->lock);
1135
1136 new_opp->opp_table = opp_table;
1119 1137
1120 ret = opp_debug_create_one(new_opp, opp_table); 1138 ret = opp_debug_create_one(new_opp, opp_table);
1121 if (ret) 1139 if (ret)
@@ -1779,6 +1797,8 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
1779 goto unlock; 1797 goto unlock;
1780 } 1798 }
1781 1799
1800 mutex_lock(&opp_table->lock);
1801
1782 /* Do we have the frequency? */ 1802 /* Do we have the frequency? */
1783 list_for_each_entry(tmp_opp, &opp_table->opp_list, node) { 1803 list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
1784 if (tmp_opp->rate == freq) { 1804 if (tmp_opp->rate == freq) {
@@ -1786,6 +1806,9 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
1786 break; 1806 break;
1787 } 1807 }
1788 } 1808 }
1809
1810 mutex_unlock(&opp_table->lock);
1811
1789 if (IS_ERR(opp)) { 1812 if (IS_ERR(opp)) {
1790 r = PTR_ERR(opp); 1813 r = PTR_ERR(opp);
1791 goto unlock; 1814 goto unlock;
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h
index 0a5eb4d1e8f7..105243b06373 100644
--- a/drivers/base/power/opp/opp.h
+++ b/drivers/base/power/opp/opp.h
@@ -131,6 +131,7 @@ enum opp_table_access {
131 * @rcu_head: RCU callback head used for deferred freeing 131 * @rcu_head: RCU callback head used for deferred freeing
132 * @dev_list: list of devices that share these OPPs 132 * @dev_list: list of devices that share these OPPs
133 * @opp_list: table of opps 133 * @opp_list: table of opps
134 * @lock: mutex protecting the opp_list.
134 * @np: struct device_node pointer for opp's DT node. 135 * @np: struct device_node pointer for opp's DT node.
135 * @clock_latency_ns_max: Max clock latency in nanoseconds. 136 * @clock_latency_ns_max: Max clock latency in nanoseconds.
136 * @shared_opp: OPP is shared between multiple devices. 137 * @shared_opp: OPP is shared between multiple devices.
@@ -163,6 +164,7 @@ struct opp_table {
163 struct rcu_head rcu_head; 164 struct rcu_head rcu_head;
164 struct list_head dev_list; 165 struct list_head dev_list;
165 struct list_head opp_list; 166 struct list_head opp_list;
167 struct mutex lock;
166 168
167 struct device_node *np; 169 struct device_node *np;
168 unsigned long clock_latency_ns_max; 170 unsigned long clock_latency_ns_max;