Re: [PATCH] ACPI/IORT: Handle PCI aliases properly

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, May 31, 2017 at 06:52:30PM +0100, 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 IORT code by mapping every RID
> produced by the alias walk, not just whichever one comes out last. Since
> adding any more internal PTR_ERR() juggling would have confused me no
> end, a bit of refactoring happens in the process - we know where to find
> the ops if everything succeeded, so we're free to just pass regular error
> codes around up until then.
> 
> Signed-off-by: Robin Murphy <robin.murphy@xxxxxxx>
> ---
> 
> This applies on top of the fixes currently queued in the IOMMU tree:
> "ACPI/IORT: Move the check to get iommu_ops from translated fwspec"
> "ACPI/IORT: Ignore all errors except EPROBE_DEFER"
> 
>  drivers/acpi/arm64/iort.c | 113 ++++++++++++++++++++++++----------------------

It is a bit hard to read (and it is certainly not your fault) but the
patch makes sense to me, I wonder whether it is not better to split it
into two patches, one refactoring the return value and second one
managing the aliases, _properly_ :), anyway:

Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@xxxxxxx>

>  1 file changed, 59 insertions(+), 54 deletions(-)
> 
> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> index 797b28dc7b34..50e2749eafdc 100644
> --- a/drivers/acpi/arm64/iort.c
> +++ b/drivers/acpi/arm64/iort.c
> @@ -598,14 +598,6 @@ void acpi_configure_pmsi_domain(struct device *dev)
>  		dev_set_msi_domain(dev, msi_domain);
>  }
>  
> -static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
> -{
> -	u32 *rid = data;
> -
> -	*rid = alias;
> -	return 0;
> -}
> -
>  static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
>  			       struct fwnode_handle *fwnode,
>  			       const struct iommu_ops *ops)
> @@ -643,8 +635,7 @@ int iort_add_device_replay(const struct iommu_ops *ops, struct device *dev)
>  {
>  	int err = 0;
>  
> -	if (!IS_ERR_OR_NULL(ops) && ops->add_device && dev->bus &&
> -	    !dev->iommu_group)
> +	if (ops->add_device && dev->bus && !dev->iommu_group)
>  		err = ops->add_device(dev);
>  
>  	return err;
> @@ -658,36 +649,49 @@ int iort_add_device_replay(const struct iommu_ops *ops, struct device *dev)
>  { return 0; }
>  #endif
>  
> -static const struct iommu_ops *iort_iommu_xlate(struct device *dev,
> -					struct acpi_iort_node *node,
> -					u32 streamid)
> +static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
> +			    u32 streamid)
>  {
> -	const struct iommu_ops *ops = NULL;
> -	int ret = -ENODEV;
> +	const struct iommu_ops *ops;
>  	struct fwnode_handle *iort_fwnode;
>  
> -	if (node) {
> -		iort_fwnode = iort_get_fwnode(node);
> -		if (!iort_fwnode)
> -			return NULL;
> +	if (!node)
> +		return -ENODEV;
>  
> -		ops = iommu_ops_from_fwnode(iort_fwnode);
> -		/*
> -		 * If the ops look-up fails, this means that either
> -		 * the SMMU drivers have not been probed yet or that
> -		 * the SMMU drivers are not built in the kernel;
> -		 * Depending on whether the SMMU drivers are built-in
> -		 * in the kernel or not, defer the IOMMU configuration
> -		 * or just abort it.
> -		 */
> -		if (!ops)
> -			return iort_iommu_driver_enabled(node->type) ?
> -			       ERR_PTR(-EPROBE_DEFER) : NULL;
> +	iort_fwnode = iort_get_fwnode(node);
> +	if (!iort_fwnode)
> +		return -ENODEV;
>  
> -		ret = arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops);
> -	}
> +	ops = iommu_ops_from_fwnode(iort_fwnode);
> +	/*
> +	 * If the ops look-up fails, this means that either
> +	 * the SMMU drivers have not been probed yet or that
> +	 * the SMMU drivers are not built in the kernel;
> +	 * Depending on whether the SMMU drivers are built-in
> +	 * in the kernel or not, defer the IOMMU configuration
> +	 * or just abort it.
> +	 */
> +	if (!ops)
> +		return iort_iommu_driver_enabled(node->type) ?
> +		       -EPROBE_DEFER : -ENODEV;
>  
> -	return ret ? NULL : ops;
> +	return arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops);
> +}
> +
> +struct iort_pci_alias_info {
> +	struct device *dev;
> +	struct acpi_iort_node *node;
> +};
> +
> +static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
> +{
> +	struct iort_pci_alias_info *info = data;
> +	struct acpi_iort_node *parent;
> +	u32 streamid;
> +
> +	parent = iort_node_map_id(info->node, alias, &streamid,
> +				  IORT_IOMMU_TYPE);
> +	return iort_iommu_xlate(info->dev, parent, streamid);
>  }
>  
>  /**
> @@ -723,7 +727,7 @@ void iort_set_dma_mask(struct device *dev)
>  const struct iommu_ops *iort_iommu_configure(struct device *dev)
>  {
>  	struct acpi_iort_node *node, *parent;
> -	const struct iommu_ops *ops = NULL;
> +	const struct iommu_ops *ops;
>  	u32 streamid = 0;
>  	int err;
>  
> @@ -737,21 +741,18 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
>  
>  	if (dev_is_pci(dev)) {
>  		struct pci_bus *bus = to_pci_dev(dev)->bus;
> -		u32 rid;
> -
> -		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
> -				       &rid);
> +		struct iort_pci_alias_info info = { .dev = dev };
>  
>  		node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
>  				      iort_match_node_callback, &bus->dev);
>  		if (!node)
>  			return NULL;
>  
> -		parent = iort_node_map_id(node, rid, &streamid,
> -					  IORT_IOMMU_TYPE);
> -
> -		ops = iort_iommu_xlate(dev, parent, streamid);
> -
> +		info.node = node;
> +		err = pci_for_each_dma_alias(to_pci_dev(dev),
> +					     iort_pci_iommu_init, &info);
> +		if (err)
> +			goto out_err;
>  	} else {
>  		int i = 0;
>  
> @@ -764,9 +765,9 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
>  						   IORT_IOMMU_TYPE, i++);
>  
>  		while (parent) {
> -			ops = iort_iommu_xlate(dev, parent, streamid);
> -			if (IS_ERR_OR_NULL(ops))
> -				return ops;
> +			err = iort_iommu_xlate(dev, parent, streamid);
> +			if (err)
> +				goto out_err;
>  
>  			parent = iort_node_map_platform_id(node, &streamid,
>  							   IORT_IOMMU_TYPE,
> @@ -774,21 +775,25 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
>  		}
>  	}
>  
> +	ops = dev->iommu_fwspec->ops;
> +
>  	/*
>  	 * If we have reason to believe the IOMMU driver missed the initial
>  	 * add_device callback for dev, replay it to get things in order.
>  	 */
>  	err = iort_add_device_replay(ops, dev);
>  	if (err)
> -		ops = ERR_PTR(err);
> -
> -	/* Ignore all other errors apart from EPROBE_DEFER */
> -	if (IS_ERR(ops) && (PTR_ERR(ops) != -EPROBE_DEFER)) {
> -		dev_dbg(dev, "Adding to IOMMU failed: %ld\n", PTR_ERR(ops));
> -		ops = NULL;
> -	}
> +		goto out_err;
>  
>  	return ops;
> +
> +	/* Ignore all other errors apart from EPROBE_DEFER */
> +out_err:
> +	if (err == -EPROBE_DEFER)
> +		return ERR_PTR(err);
> +
> +	dev_dbg(dev, "Adding to IOMMU failed: %d\n", err);
> +	return NULL;
>  }
>  
>  static void __init acpi_iort_register_irq(int hwirq, const char *name,
> -- 
> 2.12.2.dirty
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux