[PATCH 2/3] pmdomain: rockchip: remove subdomain links before the domains on error
Arun Iyengar via B4 Relay
devnull+aiyengar1990.gmail.com at kernel.org
Mon Nov 9 23:11:29 PST 2026
From: Arun Iyengar <aiyengar1990 at gmail.com>
When rockchip_pm_domain_probe() fails after some domains were
registered, for example because a clock of a nested domain is not
available yet and returns -EPROBE_DEFER, rockchip_pm_domain_cleanup()
calls pm_genpd_remove() for every registered domain in domain id order.
pm_genpd_remove() refuses a domain that is still the parent of another
domain and returns -EBUSY. The cleanup walks parents before their
children in id order, and on RK3588 RKVDEC0/RKVDEC1 (ids 14 and 15) are
also children of VDPU (id 21), so no ordering of the domain array lets
every removal succeed. Each refused parent only gets the "state may be
inconsistent" message, and on return devm frees its struct
rockchip_pm_domain, the embedded generic_pm_domain included, while it
is still linked on the global gpd_list. The next registration, the
unused domain power off at late_initcall and the genpd debugfs files
then walk freed memory.
Remove the parent/child links first, walking the power domain nodes
depth first in the same way rockchip_pm_add_subdomain() added them, so
that each child no longer has subdomains of its own when its link is
removed. With no links left, and no provider or devices attached in the
probe error path, pm_genpd_remove() succeeds for every domain before
devm frees it. pm_genpd_remove_subdomain() also drops the parent's
subdomain count, which removing the child with pm_genpd_remove() alone
does not.
Fixes: 79d12b7a7f22 ("soc: rockchip: power-domain: use pm_genpd_remove in error cleanup")
Cc: stable at vger.kernel.org
Signed-off-by: Arun Iyengar <aiyengar1990 at gmail.com>
---
drivers/pmdomain/rockchip/pm-domains.c | 46 ++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c
index 3b39b231ca0e..d7df19e10000 100644
--- a/drivers/pmdomain/rockchip/pm-domains.c
+++ b/drivers/pmdomain/rockchip/pm-domains.c
@@ -957,12 +957,58 @@ static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
/* devm will free our memory */
}
+static struct generic_pm_domain *
+rockchip_pm_node_to_genpd(struct rockchip_pmu *pmu, struct device_node *node)
+{
+ u32 id;
+
+ if (of_property_read_u32(node, "reg", &id) ||
+ id >= pmu->genpd_data.num_domains)
+ return NULL;
+
+ return pmu->genpd_data.domains[id];
+}
+
+/*
+ * Undo the pm_genpd_add_subdomain() calls of rockchip_pm_add_subdomain().
+ * Links are only ever added between a DT node and its children, so walking
+ * the tree depth first and removing the links of the children before the
+ * link to their parent always finds the subdomain without subdomains of its
+ * own, as pm_genpd_remove_subdomain() requires. This also covers domains with
+ * more than one parent (RKVDEC0/RKVDEC1 on RK3588), which a walk in domain id
+ * or registration order does not.
+ */
+static void rockchip_pm_remove_subdomains(struct rockchip_pmu *pmu,
+ struct device_node *parent)
+{
+ struct generic_pm_domain *parent_domain, *child_domain;
+
+ parent_domain = rockchip_pm_node_to_genpd(pmu, parent);
+
+ for_each_child_of_node_scoped(parent, np) {
+ rockchip_pm_remove_subdomains(pmu, np);
+
+ child_domain = rockchip_pm_node_to_genpd(pmu, np);
+ if (parent_domain && child_domain)
+ pm_genpd_remove_subdomain(parent_domain, child_domain);
+ }
+}
+
static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu)
{
struct generic_pm_domain *genpd;
struct rockchip_pm_domain *pd;
int i;
+ /*
+ * pm_genpd_remove() refuses a domain that still has subdomains, and
+ * devm frees the domains on return whether or not they were removed,
+ * which would leave freed memory on the global genpd list. Remove all
+ * parent/child links first, so every domain can be removed.
+ */
+ for_each_available_child_of_node_scoped(pmu->dev->of_node, np)
+ rockchip_pm_remove_subdomains(pmu, np);
+
for (i = 0; i < pmu->genpd_data.num_domains; i++) {
genpd = pmu->genpd_data.domains[i];
if (genpd) {
--
2.55.0
More information about the Linux-rockchip
mailing list