diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-11-27 11:24:46 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-11-27 11:24:46 -0500 |
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, | |||
338 | struct pci_dev *pdev = to_pci_dev(data); | 338 | struct pci_dev *pdev = to_pci_dev(data); |
339 | struct dmar_pci_notify_info *info; | 339 | struct dmar_pci_notify_info *info; |
340 | 340 | ||
341 | /* Only care about add/remove events for physical functions */ | 341 | /* Only care about add/remove events for physical functions. |
342 | * For VFs we actually do the lookup based on the corresponding | ||
343 | * PF in device_to_iommu() anyway. */ | ||
342 | if (pdev->is_virtfn) | 344 | if (pdev->is_virtfn) |
343 | return NOTIFY_DONE; | 345 | return NOTIFY_DONE; |
344 | if (action != BUS_NOTIFY_ADD_DEVICE && | 346 | 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 | |||
892 | return NULL; | 892 | return NULL; |
893 | 893 | ||
894 | if (dev_is_pci(dev)) { | 894 | if (dev_is_pci(dev)) { |
895 | struct pci_dev *pf_pdev; | ||
896 | |||
895 | pdev = to_pci_dev(dev); | 897 | pdev = to_pci_dev(dev); |
898 | /* VFs aren't listed in scope tables; we need to look up | ||
899 | * the PF instead to find the IOMMU. */ | ||
900 | pf_pdev = pci_physfn(pdev); | ||
901 | dev = &pf_pdev->dev; | ||
896 | segment = pci_domain_nr(pdev->bus); | 902 | segment = pci_domain_nr(pdev->bus); |
897 | } else if (has_acpi_companion(dev)) | 903 | } else if (has_acpi_companion(dev)) |
898 | dev = &ACPI_COMPANION(dev)->dev; | 904 | dev = &ACPI_COMPANION(dev)->dev; |
@@ -905,6 +911,13 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf | |||
905 | for_each_active_dev_scope(drhd->devices, | 911 | for_each_active_dev_scope(drhd->devices, |
906 | drhd->devices_cnt, i, tmp) { | 912 | drhd->devices_cnt, i, tmp) { |
907 | if (tmp == dev) { | 913 | if (tmp == dev) { |
914 | /* For a VF use its original BDF# not that of the PF | ||
915 | * which we used for the IOMMU lookup. Strictly speaking | ||
916 | * we could do this for all PCI devices; we only need to | ||
917 | * get the BDF# from the scope table for ACPI matches. */ | ||
918 | if (pdev->is_virtfn) | ||
919 | goto got_pdev; | ||
920 | |||
908 | *bus = drhd->devices[i].bus; | 921 | *bus = drhd->devices[i].bus; |
909 | *devfn = drhd->devices[i].devfn; | 922 | *devfn = drhd->devices[i].devfn; |
910 | goto out; | 923 | 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) | |||
39 | struct page *pages; | 39 | struct page *pages; |
40 | int order; | 40 | int order; |
41 | 41 | ||
42 | order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT; | 42 | /* Start at 2 because it's defined as 2^(1+PSS) */ |
43 | if (order < 0) | 43 | iommu->pasid_max = 2 << ecap_pss(iommu->ecap); |
44 | order = 0; | 44 | |
45 | 45 | /* Eventually I'm promised we will get a multi-level PASID table | |
46 | * and it won't have to be physically contiguous. Until then, | ||
47 | * limit the size because 8MiB contiguous allocations can be hard | ||
48 | * to come by. The limit of 0x20000, which is 1MiB for each of | ||
49 | * the PASID and PASID-state tables, is somewhat arbitrary. */ | ||
50 | if (iommu->pasid_max > 0x20000) | ||
51 | iommu->pasid_max = 0x20000; | ||
52 | |||
53 | order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max); | ||
46 | pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); | 54 | pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); |
47 | if (!pages) { | 55 | if (!pages) { |
48 | pr_warn("IOMMU: %s: Failed to allocate PASID table\n", | 56 | pr_warn("IOMMU: %s: Failed to allocate PASID table\n", |
@@ -53,6 +61,8 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu) | |||
53 | pr_info("%s: Allocated order %d PASID table.\n", iommu->name, order); | 61 | pr_info("%s: Allocated order %d PASID table.\n", iommu->name, order); |
54 | 62 | ||
55 | if (ecap_dis(iommu->ecap)) { | 63 | if (ecap_dis(iommu->ecap)) { |
64 | /* Just making it explicit... */ | ||
65 | BUILD_BUG_ON(sizeof(struct pasid_entry) != sizeof(struct pasid_state_entry)); | ||
56 | pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); | 66 | pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order); |
57 | if (pages) | 67 | if (pages) |
58 | iommu->pasid_state_table = page_address(pages); | 68 | iommu->pasid_state_table = page_address(pages); |
@@ -68,11 +78,7 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu) | |||
68 | 78 | ||
69 | int intel_svm_free_pasid_tables(struct intel_iommu *iommu) | 79 | int intel_svm_free_pasid_tables(struct intel_iommu *iommu) |
70 | { | 80 | { |
71 | int order; | 81 | int order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max); |
72 | |||
73 | order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT; | ||
74 | if (order < 0) | ||
75 | order = 0; | ||
76 | 82 | ||
77 | if (iommu->pasid_table) { | 83 | if (iommu->pasid_table) { |
78 | free_pages((unsigned long)iommu->pasid_table, order); | 84 | 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_ | |||
371 | } | 377 | } |
372 | svm->iommu = iommu; | 378 | svm->iommu = iommu; |
373 | 379 | ||
374 | if (pasid_max > 2 << ecap_pss(iommu->ecap)) | 380 | if (pasid_max > iommu->pasid_max) |
375 | pasid_max = 2 << ecap_pss(iommu->ecap); | 381 | pasid_max = iommu->pasid_max; |
376 | 382 | ||
377 | /* Do not use PASID 0 in caching mode (virtualised IOMMU) */ | 383 | /* Do not use PASID 0 in caching mode (virtualised IOMMU) */ |
378 | ret = idr_alloc(&iommu->pasid_idr, svm, | 384 | 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 { | |||
429 | struct page_req_dsc *prq; | 429 | struct page_req_dsc *prq; |
430 | unsigned char prq_name[16]; /* Name for PRQ interrupt */ | 430 | unsigned char prq_name[16]; /* Name for PRQ interrupt */ |
431 | struct idr pasid_idr; | 431 | struct idr pasid_idr; |
432 | u32 pasid_max; | ||
432 | #endif | 433 | #endif |
433 | struct q_inval *qi; /* Queued invalidation info */ | 434 | struct q_inval *qi; /* Queued invalidation info */ |
434 | u32 *iommu_state; /* Store iommu states between suspend and resume.*/ | 435 | u32 *iommu_state; /* Store iommu states between suspend and resume.*/ |