diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2017-01-22 23:41:41 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-01-30 03:22:20 -0500 |
commit | 37a73ec0c9bbd712d24cc2035696893e5b6119a5 (patch) | |
tree | f26471abf34316fc58ea9fbb19e5eaba2f4f0d78 | |
parent | b6160e26936bcf1b9181bb34ad4f420ccd3f39f0 (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.c | 31 | ||||
-rw-r--r-- | drivers/base/power/opp/opp.h | 2 |
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 | */ |
970 | static void _opp_remove(struct opp_table *opp_table, struct dev_pm_opp *opp) | 972 | static 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; |