diff options
Diffstat (limited to 'drivers/iommu/amd_iommu.c')
| -rw-r--r-- | drivers/iommu/amd_iommu.c | 25 | 
1 files changed, 22 insertions, 3 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 6d1cbdfc9b2..b64502dfa9f 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -296,8 +296,13 @@ static int iommu_init_device(struct device *dev)  	} else  		dma_pdev = pci_dev_get(pdev); +	/* Account for quirked devices */  	swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); +	/* +	 * If it's a multifunction device that does not support our +	 * required ACS flags, add to the same group as function 0. +	 */  	if (dma_pdev->multifunction &&  	    !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))  		swap_pci_ref(&dma_pdev, @@ -305,14 +310,28 @@ static int iommu_init_device(struct device *dev)  					  PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),  					  0))); +	/* +	 * Devices on the root bus go through the iommu.  If that's not us, +	 * find the next upstream device and test ACS up to the root bus. +	 * Finding the next device may require skipping virtual buses. +	 */  	while (!pci_is_root_bus(dma_pdev->bus)) { -		if (pci_acs_path_enabled(dma_pdev->bus->self, -					 NULL, REQ_ACS_FLAGS)) +		struct pci_bus *bus = dma_pdev->bus; + +		while (!bus->self) { +			if (!pci_is_root_bus(bus)) +				bus = bus->parent; +			else +				goto root_bus; +		} + +		if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))  			break; -		swap_pci_ref(&dma_pdev, pci_dev_get(dma_pdev->bus->self)); +		swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));  	} +root_bus:  	group = iommu_group_get(&dma_pdev->dev);  	pci_dev_put(dma_pdev);  	if (!group) {  |