diff options
| author | Linus Torvalds <[email protected]> | 2016-11-27 08:24:46 -0800 | 
|---|---|---|
| committer | Linus Torvalds <[email protected]> | 2016-11-27 08:24:46 -0800 | 
| commit | 105ecadc6d9c1effd23dd46fcc340f62d467cd6c (patch) | |
| tree | 594d2302bf4d52dedfc5ad131a6093558fb28f9e | |
| parent | ff17bf8a0d2d60a343db304b835c0e83efa660d9 (diff) | |
| parent | 910170442944e1f8674fd5ddbeeb8ccd1877ea98 (diff) | |
Merge git://git.infradead.org/intel-iommu
Pull IOMMU fixes from David Woodhouse:
 "Two minor fixes.
  The first fixes the assignment of SR-IOV virtual functions to the
  correct IOMMU unit, and the second fixes the excessively large (and
  physically contiguous) PASID tables used with SVM"
* git://git.infradead.org/intel-iommu:
  iommu/vt-d: Fix PASID table allocation
  iommu/vt-d: Fix IOMMU lookup for SR-IOV Virtual Functions
| -rw-r--r-- | drivers/iommu/dmar.c | 4 | ||||
| -rw-r--r-- | drivers/iommu/intel-iommu.c | 13 | ||||
| -rw-r--r-- | drivers/iommu/intel-svm.c | 28 | ||||
| -rw-r--r-- | include/linux/intel-iommu.h | 1 | 
4 files changed, 34 insertions, 12 deletions
| diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 58470f5ced04..8c53748a769d 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -338,7 +338,9 @@ static int dmar_pci_bus_notifier(struct notifier_block *nb,  	struct pci_dev *pdev = to_pci_dev(data);  	struct dmar_pci_notify_info *info; -	/* Only care about add/remove events for physical functions */ +	/* Only care about add/remove events for physical functions. +	 * For VFs we actually do the lookup based on the corresponding +	 * PF in device_to_iommu() anyway. */  	if (pdev->is_virtfn)  		return NOTIFY_DONE;  	if (action != BUS_NOTIFY_ADD_DEVICE && diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 3965e73db51c..d8376c2d18b3 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -892,7 +892,13 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf  		return NULL;  	if (dev_is_pci(dev)) { +		struct pci_dev *pf_pdev; +  		pdev = to_pci_dev(dev); +		/* VFs aren't listed in scope tables; we need to look up +		 * the PF instead to find the IOMMU. */ +		pf_pdev = pci_physfn(pdev); +		dev = &pf_pdev->dev;  		segment = pci_domain_nr(pdev->bus);  	} else if (has_acpi_companion(dev))  		dev = &ACPI_COMPANION(dev)->dev; @@ -905,6 +911,13 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf  		for_each_active_dev_scope(drhd->devices,  					  drhd->devices_cnt, i, tmp) {  			if (tmp == dev) { +				/* For a VF use its original BDF# not that of the PF +				 * which we used for the IOMMU lookup. Strictly speaking +				 * we could do this for all PCI devices; we only need to +				 * get the BDF# from the scope table for ACPI matches. */ +				if (pdev->is_virtfn) +					goto got_pdev; +  				*bus = drhd->devices[i].bus;  				*devfn = drhd->devices[i].devfn;  				goto out; diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index 8ebb3530afa7..cb72e0011310 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -39,10 +39,18 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)  	struct page *pages;  	int order; -	order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT; -	if (order < 0) -		order = 0; - +	/* Start at 2 because it's defined as 2^(1+PSS) */ +	iommu->pasid_max = 2 << ecap_pss(iommu->ecap); + +	/* Eventually I'm promised we will get a multi-level PASID table +	 * and it won't have to be physically contiguous. Until then, +	 * limit the size because 8MiB contiguous allocations can be hard +	 * to come by. The limit of 0x20000, which is 1MiB for each of +	 * the PASID and PASID-state tables, is somewhat arbitrary. */ +	if (iommu->pasid_max > 0x20000) +		iommu->pasid_max = 0x20000; + +	order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max);  	pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);  	if (!pages) {  		pr_warn("IOMMU: %s: Failed to allocate PASID table\n", @@ -53,6 +61,8 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)  	pr_info("%s: Allocated order %d PASID table.\n", iommu->name, order);  	if (ecap_dis(iommu->ecap)) { +		/* Just making it explicit... */ +		BUILD_BUG_ON(sizeof(struct pasid_entry) != sizeof(struct pasid_state_entry));  		pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);  		if (pages)  			iommu->pasid_state_table = page_address(pages); @@ -68,11 +78,7 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)  int intel_svm_free_pasid_tables(struct intel_iommu *iommu)  { -	int order; - -	order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT; -	if (order < 0) -		order = 0; +	int order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max);  	if (iommu->pasid_table) {  		free_pages((unsigned long)iommu->pasid_table, order); @@ -371,8 +377,8 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_  		}  		svm->iommu = iommu; -		if (pasid_max > 2 << ecap_pss(iommu->ecap)) -			pasid_max = 2 << ecap_pss(iommu->ecap); +		if (pasid_max > iommu->pasid_max) +			pasid_max = iommu->pasid_max;  		/* Do not use PASID 0 in caching mode (virtualised IOMMU) */  		ret = idr_alloc(&iommu->pasid_idr, svm, diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 2d9b650047a5..d49e26c6cdc7 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -429,6 +429,7 @@ struct intel_iommu {  	struct page_req_dsc *prq;  	unsigned char prq_name[16];    /* Name for PRQ interrupt */  	struct idr pasid_idr; +	u32 pasid_max;  #endif  	struct q_inval  *qi;            /* Queued invalidation info */  	u32 *iommu_state; /* Store iommu states between suspend and resume.*/ |