Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

OPP: Create separate kref for static OPPs list

The static OPPs don't always get freed with the OPP table, it can happen
before that as well. For example, if the OPP table is first created
using helpers like dev_pm_opp_set_supported_hw() and the OPPs are
created at a later point. Now when the OPPs are removed, the OPP table
stays until the time dev_pm_opp_put_supported_hw() is called.

Later patches will streamline the freeing of OPP table and that requires
the static OPPs to get freed with help of a separate kernel reference.
This patch prepares for that by creating a separate kref for static OPPs
list.

Tested-by: Niklas Cassel <niklas.cassel@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>

+42 -1
+32 -1
drivers/opp/core.c
··· 892 892 mutex_unlock(&opp_table_lock); 893 893 } 894 894 895 + void _opp_remove_all_static(struct opp_table *opp_table) 896 + { 897 + struct dev_pm_opp *opp, *tmp; 898 + 899 + list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) { 900 + if (!opp->dynamic) 901 + dev_pm_opp_put(opp); 902 + } 903 + 904 + opp_table->parsed_static_opps = false; 905 + } 906 + 907 + static void _opp_table_list_kref_release(struct kref *kref) 908 + { 909 + struct opp_table *opp_table = container_of(kref, struct opp_table, 910 + list_kref); 911 + 912 + _opp_remove_all_static(opp_table); 913 + mutex_unlock(&opp_table_lock); 914 + } 915 + 916 + void _put_opp_list_kref(struct opp_table *opp_table) 917 + { 918 + kref_put_mutex(&opp_table->list_kref, _opp_table_list_kref_release, 919 + &opp_table_lock); 920 + } 921 + 895 922 void dev_pm_opp_put_opp_table(struct opp_table *opp_table) 896 923 { 897 924 kref_put_mutex(&opp_table->kref, _opp_table_kref_release, ··· 1773 1746 /* Find if opp_table manages a single device */ 1774 1747 if (list_is_singular(&opp_table->dev_list)) { 1775 1748 /* Free static OPPs */ 1749 + _put_opp_list_kref(opp_table); 1750 + 1751 + /* Free dynamic OPPs */ 1776 1752 list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) { 1777 - if (remove_all || !opp->dynamic) 1753 + if (remove_all) 1778 1754 dev_pm_opp_put(opp); 1779 1755 } 1780 1756 ··· 1788 1758 if (opp_table->genpd_performance_state) 1789 1759 dev_pm_genpd_set_performance_state(dev, 0); 1790 1760 } else { 1761 + _put_opp_list_kref(opp_table); 1791 1762 _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table); 1792 1763 } 1793 1764
+7
drivers/opp/of.c
··· 411 411 ret = -ENOMEM; 412 412 else if (!opp_table->parsed_static_opps) 413 413 goto initialize_static_opps; 414 + else 415 + kref_get(&opp_table->list_kref); 414 416 415 417 goto put_opp_table; 416 418 } ··· 422 420 return -ENOMEM; 423 421 424 422 initialize_static_opps: 423 + kref_init(&opp_table->list_kref); 424 + 425 425 /* We have opp-table node now, iterate over it and add OPPs */ 426 426 for_each_available_child_of_node(opp_np, np) { 427 427 count++; ··· 441 437 /* There should be one of more OPP defined */ 442 438 if (WARN_ON(!count)) { 443 439 ret = -ENOENT; 440 + _put_opp_list_kref(opp_table); 444 441 goto put_opp_table; 445 442 } 446 443 ··· 495 490 opp_table = dev_pm_opp_get_opp_table(dev); 496 491 if (!opp_table) 497 492 return -ENOMEM; 493 + 494 + kref_init(&opp_table->list_kref); 498 495 499 496 val = prop->value; 500 497 while (nr) {
+3
drivers/opp/opp.h
··· 126 126 * @dev_list: list of devices that share these OPPs 127 127 * @opp_list: table of opps 128 128 * @kref: for reference count of the table. 129 + * @list_kref: for reference count of the OPP list. 129 130 * @lock: mutex protecting the opp_list and dev_list. 130 131 * @np: struct device_node pointer for opp's DT node. 131 132 * @clock_latency_ns_max: Max clock latency in nanoseconds. ··· 158 157 struct list_head dev_list; 159 158 struct list_head opp_list; 160 159 struct kref kref; 160 + struct kref list_kref; 161 161 struct mutex lock; 162 162 163 163 struct device_node *np; ··· 202 200 int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic); 203 201 void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of, int last_cpu); 204 202 struct opp_table *_add_opp_table(struct device *dev); 203 + void _put_opp_list_kref(struct opp_table *opp_table); 205 204 206 205 #ifdef CONFIG_OF 207 206 void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index);