When creating a consumer/supplier relationship between devices it's essential to make sure they aren't supplying each other creating a circular dependency. Introduce a new function to check if such circular dependency exists between two device nodes and use it in of_link_to_phandle(). Fixes: a3e1d1a7f5fc ("of: property: Add functional dependency link from DT bindings") Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@xxxxxxx> --- NOTE: I feel of_link_is_circular() is a little dense, and could benefit from some abstraction/refactoring. That said, I'd rather get some feedback, before spending time on it. drivers/of/property.c | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/of/property.c b/drivers/of/property.c index 2c7978ef22be1..74a5190408c3b 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1171,6 +1171,44 @@ static const struct supplier_bindings of_supplier_bindings[] = { {} }; +/** + * of_link_is_circular - Make sure potential link isn't circular + * + * @sup_np: Supplier device + * @con_np: Consumer device + * + * This function checks if @sup_np's properties contain a reference to @con_np. + * + * Will return true if there's a circular dependency and false otherwise. + */ +static bool of_link_is_circular(struct device_node *sup_np, + struct device_node *con_np) +{ + const struct supplier_bindings *s = of_supplier_bindings; + struct device_node *tmp; + bool matched = false; + struct property *p; + int i = 0; + + for_each_property_of_node(sup_np, p) { + while (!matched && s->parse_prop) { + while ((tmp = s->parse_prop(sup_np, p->name, i))) { + matched = true; + i++; + + if (tmp == con_np) + return true; + } + i = 0; + s++; + } + s = of_supplier_bindings; + matched = false; + } + + return false; +} + /** * of_link_to_phandle - Add device link to supplier from supplier phandle * @dev: consumer device @@ -1216,6 +1254,18 @@ static int of_link_to_phandle(struct device *dev, struct device_node *sup_np, return -ENODEV; } + /* + * It is possible for consumer device nodes to also supply the device + * node they are consuming from. Creating an unwarranted circular + * dependency. + */ + if (of_link_is_circular(sup_np, dev->of_node)) { + dev_dbg(dev, "Not linking to %pOFP - Circular dependency\n", + sup_np); + of_node_put(sup_np); + return -ENODEV; + } + /* * Don't allow linking a device node as a consumer of one of its * descendant nodes. By definition, a child node can't be a functional -- 2.26.0