On 31/05/17 18:52, Robin Murphy wrote: > When a PCI device has DMA quirks, we need to ensure that an upstream > IOMMU knows about all possible aliases, since the presence of a DMA > quirk does not preclude the device still also emitting transactions > (e.g. MSIs) on its 'real' RID. Similarly, the rules for bridge aliasing > are relatively complex, and some bridges may only take ownership of > transactions under particular transient circumstances, leading again to > multiple RIDs potentially being seen at the IOMMU for the given device. > > Take all this into account in the OF code by translating every RID > produced by the alias walk, not just whichever one comes out last. > Happily, this also makes things tidy enough that we can reduce the > number of both total lines of code, and confusing levels of indirection, > by pulling the "iommus"/"iommu-map" parsing helpers back in-line again.
Any comments on this? It should still apply as-is; I don't recall any conflicts when I rebased a local branch onto 4.13-rc1. Robin. > Signed-off-by: Robin Murphy <robin.mur...@arm.com> > --- > > This applies on top of the fix currently queued in the IOMMU tree: > "iommu/of: Ignore all errors except EPROBE_DEFER" > > drivers/iommu/of_iommu.c | 104 > +++++++++++++++++++++-------------------------- > 1 file changed, 47 insertions(+), 57 deletions(-) > > diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c > index 19779b88a479..1fbd7a142b30 100644 > --- a/drivers/iommu/of_iommu.c > +++ b/drivers/iommu/of_iommu.c > @@ -140,75 +140,39 @@ static const struct iommu_ops > return ops; > } > > -static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data) > -{ > - struct of_phandle_args *iommu_spec = data; > +struct of_pci_iommu_alias_info { > + struct device *dev; > + struct device_node *np; > +}; > > - iommu_spec->args[0] = alias; > - return iommu_spec->np == pdev->bus->dev.of_node; > -} > - > -static const struct iommu_ops > -*of_pci_iommu_init(struct pci_dev *pdev, struct device_node *bridge_np) > +static int of_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) > { > + struct of_pci_iommu_alias_info *info = data; > const struct iommu_ops *ops; > - struct of_phandle_args iommu_spec; > + struct of_phandle_args iommu_spec = { .args_count = 1 }; > int err; > > - /* > - * Start by tracing the RID alias down the PCI topology as > - * far as the host bridge whose OF node we have... > - * (we're not even attempting to handle multi-alias devices yet) > - */ > - iommu_spec.args_count = 1; > - iommu_spec.np = bridge_np; > - pci_for_each_dma_alias(pdev, __get_pci_rid, &iommu_spec); > - /* > - * ...then find out what that becomes once it escapes the PCI > - * bus into the system beyond, and which IOMMU it ends up at. > - */ > - iommu_spec.np = NULL; > - err = of_pci_map_rid(bridge_np, iommu_spec.args[0], "iommu-map", > + err = of_pci_map_rid(info->np, alias, "iommu-map", > "iommu-map-mask", &iommu_spec.np, > iommu_spec.args); > if (err) > - return err == -ENODEV ? NULL : ERR_PTR(err); > - > - ops = of_iommu_xlate(&pdev->dev, &iommu_spec); > + return err == -ENODEV ? 1 : err; > > + ops = of_iommu_xlate(info->dev, &iommu_spec); > of_node_put(iommu_spec.np); > - return ops; > -} > > -static const struct iommu_ops > -*of_platform_iommu_init(struct device *dev, struct device_node *np) > -{ > - struct of_phandle_args iommu_spec; > - const struct iommu_ops *ops = NULL; > - int idx = 0; > + if (IS_ERR(ops)) > + return PTR_ERR(ops); > > - /* > - * We don't currently walk up the tree looking for a parent IOMMU. > - * See the `Notes:' section of > - * Documentation/devicetree/bindings/iommu/iommu.txt > - */ > - while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells", > - idx, &iommu_spec)) { > - ops = of_iommu_xlate(dev, &iommu_spec); > - of_node_put(iommu_spec.np); > - idx++; > - if (IS_ERR_OR_NULL(ops)) > - break; > - } > - > - return ops; > + return info->np == pdev->bus->dev.of_node; > } > > const struct iommu_ops *of_iommu_configure(struct device *dev, > struct device_node *master_np) > { > - const struct iommu_ops *ops; > + const struct iommu_ops *ops = NULL; > struct iommu_fwspec *fwspec = dev->iommu_fwspec; > + int err; > > if (!master_np) > return NULL; > @@ -221,18 +185,44 @@ const struct iommu_ops *of_iommu_configure(struct > device *dev, > iommu_fwspec_free(dev); > } > > - if (dev_is_pci(dev)) > - ops = of_pci_iommu_init(to_pci_dev(dev), master_np); > - else > - ops = of_platform_iommu_init(dev, master_np); > + /* > + * We don't currently walk up the tree looking for a parent IOMMU. > + * See the `Notes:' section of > + * Documentation/devicetree/bindings/iommu/iommu.txt > + */ > + if (dev_is_pci(dev)) { > + struct of_pci_iommu_alias_info info = { > + .dev = dev, > + .np = master_np, > + }; > + > + err = pci_for_each_dma_alias(to_pci_dev(dev), > + of_pci_iommu_init, &info); > + if (err) /* err > 0 means the walk stopped, but non-fatally */ > + ops = ERR_PTR(min(err, 0)); > + else /* success implies both fwspec and ops are now valid */ > + ops = dev->iommu_fwspec->ops; > + } else { > + struct of_phandle_args iommu_spec; > + int idx = 0; > + > + while (!of_parse_phandle_with_args(master_np, "iommus", > + "#iommu-cells", > + idx, &iommu_spec)) { > + ops = of_iommu_xlate(dev, &iommu_spec); > + of_node_put(iommu_spec.np); > + idx++; > + if (IS_ERR_OR_NULL(ops)) > + break; > + } > + } > /* > * If we have reason to believe the IOMMU driver missed the initial > * add_device callback for dev, replay it to get things in order. > */ > if (!IS_ERR_OR_NULL(ops) && ops->add_device && > dev->bus && !dev->iommu_group) { > - int err = ops->add_device(dev); > - > + err = ops->add_device(dev); > if (err) > ops = ERR_PTR(err); > } >