aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2017-01-22 23:41:42 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-01-30 03:22:20 -0500
commitf067a982cefa8df5642212bb0c7e25974831f781 (patch)
treeabc4cc23a4e49cb000104b09182ec4defab569dd
parent37a73ec0c9bbd712d24cc2035696893e5b6119a5 (diff)
PM / OPP: Add 'struct kref' to OPP table
Add kref to struct opp_table for easier accounting of the OPP table. Note that the new routine dev_pm_opp_get_opp_table() takes the reference from under the opp_table_lock, which guarantees that the OPP table doesn't get freed unless dev_pm_opp_put_opp_table() is called for the OPP table. Two separate release mechanisms are added: locked and unlocked. In unlocked version the routines aren't required to take/drop opp_table_lock as the callers have already done that. This is required to avoid breaking git bisect, otherwise we may get lockdeps between commits. Once all the users of OPP table are updated the unlocked version shall be removed. 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.c51
-rw-r--r--drivers/base/power/opp/opp.h3
-rw-r--r--include/linux/pm_opp.h10
3 files changed, 62 insertions, 2 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index dcebd5efb6a1..ccc0d8913fd0 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -855,6 +855,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev)
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 mutex_init(&opp_table->lock);
858 kref_init(&opp_table->kref);
858 859
859 /* Secure the device table modification */ 860 /* Secure the device table modification */
860 list_add_rcu(&opp_table->node, &opp_tables); 861 list_add_rcu(&opp_table->node, &opp_tables);
@@ -894,8 +895,36 @@ static void _kfree_device_rcu(struct rcu_head *head)
894 kfree_rcu(opp_table, rcu_head); 895 kfree_rcu(opp_table, rcu_head);
895} 896}
896 897
897static void _free_opp_table(struct opp_table *opp_table) 898void _get_opp_table_kref(struct opp_table *opp_table)
898{ 899{
900 kref_get(&opp_table->kref);
901}
902
903struct opp_table *dev_pm_opp_get_opp_table(struct device *dev)
904{
905 struct opp_table *opp_table;
906
907 /* Hold our table modification lock here */
908 mutex_lock(&opp_table_lock);
909
910 opp_table = _find_opp_table(dev);
911 if (!IS_ERR(opp_table)) {
912 _get_opp_table_kref(opp_table);
913 goto unlock;
914 }
915
916 opp_table = _allocate_opp_table(dev);
917
918unlock:
919 mutex_unlock(&opp_table_lock);
920
921 return opp_table;
922}
923EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_table);
924
925static void _opp_table_kref_release_unlocked(struct kref *kref)
926{
927 struct opp_table *opp_table = container_of(kref, struct opp_table, kref);
899 struct opp_device *opp_dev; 928 struct opp_device *opp_dev;
900 929
901 /* Release clk */ 930 /* Release clk */
@@ -916,6 +945,24 @@ static void _free_opp_table(struct opp_table *opp_table)
916 _kfree_device_rcu); 945 _kfree_device_rcu);
917} 946}
918 947
948static void dev_pm_opp_put_opp_table_unlocked(struct opp_table *opp_table)
949{
950 kref_put(&opp_table->kref, _opp_table_kref_release_unlocked);
951}
952
953static void _opp_table_kref_release(struct kref *kref)
954{
955 _opp_table_kref_release_unlocked(kref);
956 mutex_unlock(&opp_table_lock);
957}
958
959void dev_pm_opp_put_opp_table(struct opp_table *opp_table)
960{
961 kref_put_mutex(&opp_table->kref, _opp_table_kref_release,
962 &opp_table_lock);
963}
964EXPORT_SYMBOL_GPL(dev_pm_opp_put_opp_table);
965
919/** 966/**
920 * _remove_opp_table() - Removes a OPP table 967 * _remove_opp_table() - Removes a OPP table
921 * @opp_table: OPP table to be removed. 968 * @opp_table: OPP table to be removed.
@@ -939,7 +986,7 @@ static void _remove_opp_table(struct opp_table *opp_table)
939 if (opp_table->set_opp) 986 if (opp_table->set_opp)
940 return; 987 return;
941 988
942 _free_opp_table(opp_table); 989 dev_pm_opp_put_opp_table_unlocked(opp_table);
943} 990}
944 991
945void _opp_free(struct dev_pm_opp *opp) 992void _opp_free(struct dev_pm_opp *opp)
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h
index 105243b06373..aae4d8f480ef 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 * @kref: for reference count of the table.
134 * @lock: mutex protecting the opp_list. 135 * @lock: mutex protecting the opp_list.
135 * @np: struct device_node pointer for opp's DT node. 136 * @np: struct device_node pointer for opp's DT node.
136 * @clock_latency_ns_max: Max clock latency in nanoseconds. 137 * @clock_latency_ns_max: Max clock latency in nanoseconds.
@@ -164,6 +165,7 @@ struct opp_table {
164 struct rcu_head rcu_head; 165 struct rcu_head rcu_head;
165 struct list_head dev_list; 166 struct list_head dev_list;
166 struct list_head opp_list; 167 struct list_head opp_list;
168 struct kref kref;
167 struct mutex lock; 169 struct mutex lock;
168 170
169 struct device_node *np; 171 struct device_node *np;
@@ -192,6 +194,7 @@ struct opp_table {
192}; 194};
193 195
194/* Routines internal to opp core */ 196/* Routines internal to opp core */
197void _get_opp_table_kref(struct opp_table *opp_table);
195struct opp_table *_find_opp_table(struct device *dev); 198struct opp_table *_find_opp_table(struct device *dev);
196struct opp_table *_add_opp_table(struct device *dev); 199struct opp_table *_add_opp_table(struct device *dev);
197struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table); 200struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table);
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 66a02deeb03f..d867c6b25f9a 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -78,6 +78,9 @@ struct dev_pm_set_opp_data {
78 78
79#if defined(CONFIG_PM_OPP) 79#if defined(CONFIG_PM_OPP)
80 80
81struct opp_table *dev_pm_opp_get_opp_table(struct device *dev);
82void dev_pm_opp_put_opp_table(struct opp_table *opp_table);
83
81unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); 84unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
82 85
83unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); 86unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp);
@@ -126,6 +129,13 @@ int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
126void dev_pm_opp_remove_table(struct device *dev); 129void dev_pm_opp_remove_table(struct device *dev);
127void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask); 130void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask);
128#else 131#else
132static inline struct opp_table *dev_pm_opp_get_opp_table(struct device *dev)
133{
134 return ERR_PTR(-ENOTSUPP);
135}
136
137static inline void dev_pm_opp_put_opp_table(struct opp_table *opp_table) {}
138
129static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) 139static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
130{ 140{
131 return 0; 141 return 0;