Skip to content

Commit 933fcd0

Browse files
LuBaolujoergroedel
authored andcommitted
iommu/vt-d: Add iotlb_sync_map callback
Some Intel VT-d hardware implementations don't support memory coherency for page table walk (presented by the Page-Walk-coherency bit in the ecap register), so that software must flush the corresponding CPU cache lines explicitly after each page table entry update. The iommu_map_sg() code iterates through the given scatter-gather list and invokes iommu_map() for each element in the scatter-gather list, which calls into the vendor IOMMU driver through iommu_ops callback. As the result, a single sg mapping may lead to multiple cache line flushes, which leads to the degradation of I/O performance after the commit <c588072bba6b5> ("iommu/vt-d: Convert intel iommu driver to the iommu ops"). Fix this by adding iotlb_sync_map callback and centralizing the clflush operations after all sg mappings. Fixes: c588072 ("iommu/vt-d: Convert intel iommu driver to the iommu ops") Reported-by: Chuck Lever <[email protected]> Link: https://lore.kernel.org/linux-iommu/[email protected]/ Signed-off-by: Lu Baolu <[email protected]> Cc: Robin Murphy <[email protected]> [ cel: removed @first_pte, which is no longer used ] Signed-off-by: Chuck Lever <[email protected]> Link: https://lore.kernel.org/linux-iommu/161177763962.1311.15577661784296014186.stgit@manet.1015granger.net Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 010bf56 commit 933fcd0

File tree

1 file changed

+60
-30
lines changed

1 file changed

+60
-30
lines changed

drivers/iommu/intel/iommu.c

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,9 +2283,9 @@ static int
22832283
__domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
22842284
unsigned long phys_pfn, unsigned long nr_pages, int prot)
22852285
{
2286-
struct dma_pte *first_pte = NULL, *pte = NULL;
22872286
unsigned int largepage_lvl = 0;
22882287
unsigned long lvl_pages = 0;
2288+
struct dma_pte *pte = NULL;
22892289
phys_addr_t pteval;
22902290
u64 attr;
22912291

@@ -2314,7 +2314,7 @@ __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
23142314
largepage_lvl = hardware_largepage_caps(domain, iov_pfn,
23152315
phys_pfn, nr_pages);
23162316

2317-
first_pte = pte = pfn_to_dma_pte(domain, iov_pfn, &largepage_lvl);
2317+
pte = pfn_to_dma_pte(domain, iov_pfn, &largepage_lvl);
23182318
if (!pte)
23192319
return -ENOMEM;
23202320
/* It is large page*/
@@ -2375,34 +2375,14 @@ __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
23752375
* recalculate 'pte' and switch back to smaller pages for the
23762376
* end of the mapping, if the trailing size is not enough to
23772377
* use another superpage (i.e. nr_pages < lvl_pages).
2378+
*
2379+
* We leave clflush for the leaf pte changes to iotlb_sync_map()
2380+
* callback.
23782381
*/
23792382
pte++;
23802383
if (!nr_pages || first_pte_in_page(pte) ||
2381-
(largepage_lvl > 1 && nr_pages < lvl_pages)) {
2382-
domain_flush_cache(domain, first_pte,
2383-
(void *)pte - (void *)first_pte);
2384+
(largepage_lvl > 1 && nr_pages < lvl_pages))
23842385
pte = NULL;
2385-
}
2386-
}
2387-
2388-
return 0;
2389-
}
2390-
2391-
static int
2392-
domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
2393-
unsigned long phys_pfn, unsigned long nr_pages, int prot)
2394-
{
2395-
int iommu_id, ret;
2396-
struct intel_iommu *iommu;
2397-
2398-
/* Do the real mapping first */
2399-
ret = __domain_mapping(domain, iov_pfn, phys_pfn, nr_pages, prot);
2400-
if (ret)
2401-
return ret;
2402-
2403-
for_each_domain_iommu(iommu_id, domain) {
2404-
iommu = g_iommus[iommu_id];
2405-
__mapping_notify_one(iommu, domain, iov_pfn, nr_pages);
24062386
}
24072387

24082388
return 0;
@@ -4943,7 +4923,6 @@ static int intel_iommu_map(struct iommu_domain *domain,
49434923
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
49444924
u64 max_addr;
49454925
int prot = 0;
4946-
int ret;
49474926

49484927
if (iommu_prot & IOMMU_READ)
49494928
prot |= DMA_PTE_READ;
@@ -4969,9 +4948,8 @@ static int intel_iommu_map(struct iommu_domain *domain,
49694948
/* Round up size to next multiple of PAGE_SIZE, if it and
49704949
the low bits of hpa would take us onto the next page */
49714950
size = aligned_nrpages(hpa, size);
4972-
ret = domain_mapping(dmar_domain, iova >> VTD_PAGE_SHIFT,
4973-
hpa >> VTD_PAGE_SHIFT, size, prot);
4974-
return ret;
4951+
return __domain_mapping(dmar_domain, iova >> VTD_PAGE_SHIFT,
4952+
hpa >> VTD_PAGE_SHIFT, size, prot);
49754953
}
49764954

49774955
static size_t intel_iommu_unmap(struct iommu_domain *domain,
@@ -5424,6 +5402,57 @@ static bool risky_device(struct pci_dev *pdev)
54245402
return false;
54255403
}
54265404

5405+
static void clflush_sync_map(struct dmar_domain *domain, unsigned long clf_pfn,
5406+
unsigned long clf_pages)
5407+
{
5408+
struct dma_pte *first_pte = NULL, *pte = NULL;
5409+
unsigned long lvl_pages = 0;
5410+
int level = 0;
5411+
5412+
while (clf_pages > 0) {
5413+
if (!pte) {
5414+
level = 0;
5415+
pte = pfn_to_dma_pte(domain, clf_pfn, &level);
5416+
if (WARN_ON(!pte))
5417+
return;
5418+
first_pte = pte;
5419+
lvl_pages = lvl_to_nr_pages(level);
5420+
}
5421+
5422+
if (WARN_ON(!lvl_pages || clf_pages < lvl_pages))
5423+
return;
5424+
5425+
clf_pages -= lvl_pages;
5426+
clf_pfn += lvl_pages;
5427+
pte++;
5428+
5429+
if (!clf_pages || first_pte_in_page(pte) ||
5430+
(level > 1 && clf_pages < lvl_pages)) {
5431+
domain_flush_cache(domain, first_pte,
5432+
(void *)pte - (void *)first_pte);
5433+
pte = NULL;
5434+
}
5435+
}
5436+
}
5437+
5438+
static void intel_iommu_iotlb_sync_map(struct iommu_domain *domain,
5439+
unsigned long iova, size_t size)
5440+
{
5441+
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
5442+
unsigned long pages = aligned_nrpages(iova, size);
5443+
unsigned long pfn = iova >> VTD_PAGE_SHIFT;
5444+
struct intel_iommu *iommu;
5445+
int iommu_id;
5446+
5447+
if (!dmar_domain->iommu_coherency)
5448+
clflush_sync_map(dmar_domain, pfn, pages);
5449+
5450+
for_each_domain_iommu(iommu_id, dmar_domain) {
5451+
iommu = g_iommus[iommu_id];
5452+
__mapping_notify_one(iommu, dmar_domain, pfn, pages);
5453+
}
5454+
}
5455+
54275456
const struct iommu_ops intel_iommu_ops = {
54285457
.capable = intel_iommu_capable,
54295458
.domain_alloc = intel_iommu_domain_alloc,
@@ -5436,6 +5465,7 @@ const struct iommu_ops intel_iommu_ops = {
54365465
.aux_detach_dev = intel_iommu_aux_detach_device,
54375466
.aux_get_pasid = intel_iommu_aux_get_pasid,
54385467
.map = intel_iommu_map,
5468+
.iotlb_sync_map = intel_iommu_iotlb_sync_map,
54395469
.unmap = intel_iommu_unmap,
54405470
.flush_iotlb_all = intel_flush_iotlb_all,
54415471
.iotlb_sync = intel_iommu_tlb_sync,

0 commit comments

Comments
 (0)