opp: Don't drop reference for an OPP table that was never parsed
authorViresh Kumar <viresh.kumar@linaro.org>
Mon, 31 Aug 2020 07:33:06 +0000 (13:03 +0530)
committerViresh Kumar <viresh.kumar@linaro.org>
Mon, 31 Aug 2020 09:19:47 +0000 (14:49 +0530)
dev_pm_opp_remove_table() should drop a reference to the OPP table only
if the DT OPP table was parsed earlier with a call to
dev_pm_opp_of_add_table() earlier. Else it may end up dropping the
reference to the OPP table, which was added as a result of other calls
like dev_pm_opp_set_clkname(). And would hence result in undesirable
behavior later on when caller would try to free the resource again.

Fixes: 03758d60265c ("opp: Replace list_kref with a local counter")
Reported-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Reported-by: Anders Roxell <anders.roxell@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
drivers/opp/core.c
drivers/opp/opp.h

index 9d7fb45..31ce5d1 100644 (file)
@@ -1291,13 +1291,19 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
 
-void _opp_remove_all_static(struct opp_table *opp_table)
+bool _opp_remove_all_static(struct opp_table *opp_table)
 {
        struct dev_pm_opp *opp, *tmp;
+       bool ret = true;
 
        mutex_lock(&opp_table->lock);
 
-       if (!opp_table->parsed_static_opps || --opp_table->parsed_static_opps)
+       if (!opp_table->parsed_static_opps) {
+               ret = false;
+               goto unlock;
+       }
+
+       if (--opp_table->parsed_static_opps)
                goto unlock;
 
        list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
@@ -1307,6 +1313,8 @@ void _opp_remove_all_static(struct opp_table *opp_table)
 
 unlock:
        mutex_unlock(&opp_table->lock);
+
+       return ret;
 }
 
 /**
@@ -2409,13 +2417,15 @@ void _dev_pm_opp_find_and_remove_table(struct device *dev)
                return;
        }
 
-       _opp_remove_all_static(opp_table);
+       /*
+        * Drop the extra reference only if the OPP table was successfully added
+        * with dev_pm_opp_of_add_table() earlier.
+        **/
+       if (_opp_remove_all_static(opp_table))
+               dev_pm_opp_put_opp_table(opp_table);
 
        /* Drop reference taken by _find_opp_table() */
        dev_pm_opp_put_opp_table(opp_table);
-
-       /* Drop reference taken while the OPP table was added */
-       dev_pm_opp_put_opp_table(opp_table);
 }
 
 /**
index e51646f..c3fcd57 100644 (file)
@@ -212,7 +212,7 @@ struct opp_table {
 
 /* Routines internal to opp core */
 void dev_pm_opp_get(struct dev_pm_opp *opp);
-void _opp_remove_all_static(struct opp_table *opp_table);
+bool _opp_remove_all_static(struct opp_table *opp_table);
 void _get_opp_table_kref(struct opp_table *opp_table);
 int _get_opp_count(struct opp_table *opp_table);
 struct opp_table *_find_opp_table(struct device *dev);