[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20201025221735.3062-47-digetx@gmail.com>
Date: Mon, 26 Oct 2020 01:17:29 +0300
From: Dmitry Osipenko <digetx@...il.com>
To: Thierry Reding <thierry.reding@...il.com>,
Jonathan Hunter <jonathanh@...dia.com>,
Georgi Djakov <georgi.djakov@...aro.org>,
Rob Herring <robh+dt@...nel.org>,
Michael Turquette <mturquette@...libre.com>,
Stephen Boyd <sboyd@...nel.org>,
Peter De Schrijver <pdeschrijver@...dia.com>,
MyungJoo Ham <myungjoo.ham@...sung.com>,
Kyungmin Park <kyungmin.park@...sung.com>,
Chanwoo Choi <cw00.choi@...sung.com>,
Mikko Perttunen <cyndis@...si.fi>,
Viresh Kumar <vireshk@...nel.org>,
Peter Geis <pgwipeout@...il.com>,
Nicolas Chauvet <kwizart@...il.com>,
Krzysztof Kozlowski <krzk@...nel.org>
Cc: linux-tegra@...r.kernel.org, linux-pm@...r.kernel.org,
linux-kernel@...r.kernel.org, dri-devel@...ts.freedesktop.org,
devicetree@...r.kernel.org
Subject: [PATCH v6 46/52] opp: Put interconnect paths outside of opp_table_lock
This patch fixes lockup which happens when OPP table is released if
interconnect provider uses OPP in the icc_provider->set() callback
and bandwidth of the ICC path is set to 0 by the ICC core when path
is released. The icc_put() doesn't need the opp_table_lock protection,
hence let's move it outside of the lock in order to resolve the problem.
In particular this fixes tegra-devfreq driver lockup on trying to unload
the driver module. The devfreq driver uses OPP-bandwidth API and its ICC
provider also uses OPP for DVFS, hence they both take same opp_table_lock
when OPP table of the devfreq is released.
Signed-off-by: Dmitry Osipenko <digetx@...il.com>
---
drivers/opp/core.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 2483e765318a..1134df360fe0 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -1187,12 +1187,6 @@ static void _opp_table_kref_release(struct kref *kref)
if (!IS_ERR(opp_table->clk))
clk_put(opp_table->clk);
- if (opp_table->paths) {
- for (i = 0; i < opp_table->path_count; i++)
- icc_put(opp_table->paths[i]);
- kfree(opp_table->paths);
- }
-
WARN_ON(!list_empty(&opp_table->opp_list));
list_for_each_entry_safe(opp_dev, temp, &opp_table->dev_list, node) {
@@ -1209,9 +1203,22 @@ static void _opp_table_kref_release(struct kref *kref)
mutex_destroy(&opp_table->genpd_virt_dev_lock);
mutex_destroy(&opp_table->lock);
list_del(&opp_table->node);
- kfree(opp_table);
mutex_unlock(&opp_table_lock);
+
+ /*
+ * Interconnect provider may use OPP too, hence icc_put() needs to be
+ * invoked outside of the opp_table_lock in order to prevent nested
+ * locking which happens when bandwidth of the ICC path is set to 0
+ * by ICC core on release of the path.
+ */
+ if (opp_table->paths) {
+ for (i = 0; i < opp_table->path_count; i++)
+ icc_put(opp_table->paths[i]);
+ kfree(opp_table->paths);
+ }
+
+ kfree(opp_table);
}
void dev_pm_opp_put_opp_table(struct opp_table *opp_table)
--
2.27.0
Powered by blists - more mailing lists