diff options
| author | Fuad Tabba <fuad.tabba@linux.dev> | 2026-09-07 12:29:44 +0100 |
|---|---|---|
| committer | Rob Herring (Arm) <robh@kernel.org> | 2026-09-28 08:55:14 -0500 |
| commit | 833aaa4790eee9812269b776eb8672acf1524741 (patch) | |
| tree | 738665f0854f76415bed1139ea99e91f3078ec08 /drivers/of | |
| parent | cbe0f79dcab1c121d767927b53a61eadf2c5b518 (diff) | |
| download | linux-next-833aaa4790eee9812269b776eb8672acf1524741.tar.gz linux-next-833aaa4790eee9812269b776eb8672acf1524741.zip | |
of/irq: Stop the MSI walk at the first msi-parent
of_check_msi_parent() returns -EINVAL both when the msi-parent names a
controller other than the one the caller filters on and when #msi-cells
isn't 0, so of_msi_xlate() can't tell the two apart and carries on up
the hierarchy. An ancestor's msi-map then maps the device onto a
controller its own node didn't name.
Hand the parsed specifier back to of_msi_xlate(), which owns the
reference as it already does on the msi-map path, and end the walk at
any node that declares an msi-parent.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260904074800.875391F00A3F@smtp.kernel.org/
Suggested-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Link: https://lore.kernel.org/all/apqnaNmeuUjfC8Ng@red-moon/
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
Link: https://patch.msgid.link/20260907112944.313618-3-fuad.tabba@linux.dev
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
Diffstat (limited to 'drivers/of')
| -rw-r--r-- | drivers/of/irq.c | 39 |
1 files changed, 22 insertions, 17 deletions
diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 109c54f34647..4fbbd13f4d0a 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -765,34 +765,27 @@ err: } } -static int of_check_msi_parent(struct device_node *dev_node, struct device_node **msi_node) +static int of_check_msi_parent(struct device_node *dev_node, struct device_node **msi_node, + struct of_phandle_args *msi_spec) { - struct of_phandle_args msi_spec; int ret; /* * An msi-parent phandle with a missing or == 0 #msi-cells * property identifies a 1:1 ID translation mapping. * - * Set the msi controller node if the firmware matches this - * condition. + * @msi_spec keeps a reference to the target node whenever the + * phandle parses, -EINVAL included, and the caller releases it. */ ret = of_parse_phandle_with_optional_args(dev_node, "msi-parent", "#msi-cells", - 0, &msi_spec); + 0, msi_spec); if (ret) return ret; - if ((*msi_node && *msi_node != msi_spec.np) || msi_spec.args_count != 0) - ret = -EINVAL; - - if (!ret && !*msi_node) { - /* Return with a node reference held */ - *msi_node = msi_spec.np; - return 0; - } - of_node_put(msi_spec.np); + if ((*msi_node && *msi_node != msi_spec->np) || msi_spec->args_count != 0) + return -EINVAL; - return ret; + return 0; } /** @@ -806,7 +799,9 @@ static int of_check_msi_parent(struct device_node *dev_node, struct device_node * @id_in: Device ID. * * Walk up the device hierarchy looking for devices with a "msi-map" - * or "msi-parent" property. If found, apply the mapping to @id_in. + * or "msi-parent" property. If found, apply the mapping to @id_in. With + * @msi_np non-NULL, a device declaring an msi-parent ends the walk, usable + * or not. * * Returns: The mapped MSI id. */ @@ -821,6 +816,7 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { struct of_phandle_args msi_spec = {}; + int ret; if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &msi_spec)) { if (msi_spec.np) { @@ -835,8 +831,17 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) break; } /* -ENODEV: msi-map absent → check for msi-parent */ - if (msi_np && !of_check_msi_parent(parent_dev->of_node, msi_np)) + if (!msi_np) + continue; + + ret = of_check_msi_parent(parent_dev->of_node, msi_np, &msi_spec); + if (msi_spec.np) { + /* A declared msi-parent names the controller, usable or not */ + if (!ret && !*msi_np) + *msi_np = of_node_get(msi_spec.np); + of_node_put(msi_spec.np); break; + } } return id_out; } |
