Support dmar_domain own multiple devices from different iommus, which are set in iommu bitmap. add function domain_get_iommu() to get the only one iommu of domain in native VT-d usage.
Signed-off-by: Weidong Han <[EMAIL PROTECTED]> --- drivers/pci/intel-iommu.c | 102 ++++++++++++++++++++++++++++------------ include/linux/dma_remapping.h | 2 +- 2 files changed, 72 insertions(+), 32 deletions(-) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 5c8baa4..39c5e9d 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -64,6 +64,7 @@ struct deferred_flush_tables { int next; struct iova *iova[HIGH_WATER_MARK]; struct dmar_domain *domain[HIGH_WATER_MARK]; + struct intel_iommu *iommu; }; static struct deferred_flush_tables *deferred_flush; @@ -184,6 +185,21 @@ void free_iova_mem(struct iova *iova) kmem_cache_free(iommu_iova_cache, iova); } +/* in native case, each domain is related to only one iommu */ +static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain) +{ + struct dmar_drhd_unit *drhd; + + for_each_drhd_unit(drhd) { + if (drhd->ignored) + continue; + if (test_bit(drhd->iommu->seq_id, &domain->iommu_bmp)) + return drhd->iommu; + } + + return NULL; +} + /* Gets context entry for a given bus and devfn */ static struct context_entry * device_to_context_entry(struct intel_iommu *iommu, u8 bus, u8 devfn) @@ -324,6 +340,7 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) int level = agaw_to_level(domain->agaw); int offset; unsigned long flags; + struct intel_iommu *iommu = domain_get_iommu(domain); BUG_ON(!domain->pgd); @@ -347,7 +364,7 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) flags); return NULL; } - __iommu_flush_cache(domain->iommu, tmp_page, + __iommu_flush_cache(iommu, tmp_page, PAGE_SIZE); dma_set_pte_addr(*pte, virt_to_phys(tmp_page)); /* @@ -356,7 +373,7 @@ static struct dma_pte * addr_to_dma_pte(struct dmar_domain *domain, u64 addr) */ dma_set_pte_readable(*pte); dma_set_pte_writable(*pte); - __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); + __iommu_flush_cache(iommu, pte, sizeof(*pte)); } parent = phys_to_virt(dma_pte_addr(*pte)); level--; @@ -393,13 +410,14 @@ static struct dma_pte *dma_addr_level_pte(struct dmar_domain *domain, u64 addr, static void dma_pte_clear_one(struct dmar_domain *domain, u64 addr) { struct dma_pte *pte = NULL; + struct intel_iommu *iommu = domain_get_iommu(domain); /* get last level pte */ pte = dma_addr_level_pte(domain, addr, 1); if (pte) { dma_clear_pte(*pte); - __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); + __iommu_flush_cache(iommu, pte, sizeof(*pte)); } } @@ -428,6 +446,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, int addr_width = agaw_to_width(domain->agaw); struct dma_pte *pte; int total = agaw_to_level(domain->agaw); + struct intel_iommu *iommu = domain_get_iommu(domain); int level; u64 tmp; @@ -447,7 +466,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, free_pgtable_page( phys_to_virt(dma_pte_addr(*pte))); dma_clear_pte(*pte); - __iommu_flush_cache(domain->iommu, + __iommu_flush_cache(iommu, pte, sizeof(*pte)); } tmp += level_size(level); @@ -1006,7 +1025,8 @@ static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu) set_bit(num, iommu->domain_ids); domain->id = num; - domain->iommu = iommu; + memset(&domain->iommu_bmp, 0, sizeof(unsigned long)); + set_bit(iommu->seq_id, &domain->iommu_bmp); iommu->domains[num] = domain; spin_unlock_irqrestore(&iommu->lock, flags); @@ -1016,10 +1036,12 @@ static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu) static void iommu_free_domain(struct dmar_domain *domain) { unsigned long flags; + struct intel_iommu *iommu; - spin_lock_irqsave(&domain->iommu->lock, flags); - clear_bit(domain->id, domain->iommu->domain_ids); - spin_unlock_irqrestore(&domain->iommu->lock, flags); + iommu = domain_get_iommu(domain); + spin_lock_irqsave(&iommu->lock, flags); + clear_bit(domain->id, iommu->domain_ids); + spin_unlock_irqrestore(&iommu->lock, flags); } static struct iova_domain reserved_iova_list; @@ -1098,7 +1120,7 @@ static int domain_init(struct dmar_domain *domain, int guest_width) domain_reserve_special_ranges(domain); /* calculate AGAW */ - iommu = domain->iommu; + iommu = domain_get_iommu(domain); if (guest_width > cap_mgaw(iommu->cap)) guest_width = cap_mgaw(iommu->cap); domain->gaw = guest_width; @@ -1151,7 +1173,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, u8 bus, u8 devfn) { struct context_entry *context; - struct intel_iommu *iommu = domain->iommu; + struct intel_iommu *iommu = domain_get_iommu(domain); unsigned long flags; pr_debug("Set context mapping for %02x:%02x.%d\n", @@ -1223,8 +1245,9 @@ static int domain_context_mapped(struct dmar_domain *domain, { int ret; struct pci_dev *tmp, *parent; + struct intel_iommu *iommu = domain_get_iommu(domain); - ret = device_context_mapped(domain->iommu, + ret = device_context_mapped(iommu, pdev->bus->number, pdev->devfn); if (!ret) return ret; @@ -1235,17 +1258,17 @@ static int domain_context_mapped(struct dmar_domain *domain, /* Secondary interface's bus number and devfn 0 */ parent = pdev->bus->self; while (parent != tmp) { - ret = device_context_mapped(domain->iommu, parent->bus->number, + ret = device_context_mapped(iommu, parent->bus->number, parent->devfn); if (!ret) return ret; parent = parent->bus->self; } if (tmp->is_pcie) - return device_context_mapped(domain->iommu, + return device_context_mapped(iommu, tmp->subordinate->number, 0); else - return device_context_mapped(domain->iommu, + return device_context_mapped(iommu, tmp->bus->number, tmp->devfn); } @@ -1257,6 +1280,7 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, struct dma_pte *pte; int index; int addr_width = agaw_to_width(domain->agaw); + struct intel_iommu *iommu = domain_get_iommu(domain); hpa &= (((u64)1) << addr_width) - 1; @@ -1276,7 +1300,7 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, BUG_ON(dma_pte_addr(*pte)); dma_set_pte_addr(*pte, start_pfn << VTD_PAGE_SHIFT); dma_set_pte_prot(*pte, prot); - __iommu_flush_cache(domain->iommu, pte, sizeof(*pte)); + __iommu_flush_cache(iommu, pte, sizeof(*pte)); start_pfn++; index++; } @@ -1285,10 +1309,12 @@ domain_page_mapping(struct dmar_domain *domain, dma_addr_t iova, static void detach_domain_for_dev(struct dmar_domain *domain, u8 bus, u8 devfn) { - clear_context_table(domain->iommu, bus, devfn); - domain->iommu->flush.flush_context(domain->iommu, 0, 0, 0, + struct intel_iommu *iommu = domain_get_iommu(domain); + + clear_context_table(iommu, bus, devfn); + iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL, 0); - domain->iommu->flush.flush_iotlb(domain->iommu, 0, 0, 0, + iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH, 0); } @@ -1827,6 +1853,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, struct iova *iova; int prot = 0; int ret; + struct intel_iommu *iommu; BUG_ON(dir == DMA_NONE); if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO) @@ -1835,7 +1862,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, domain = get_valid_domain_for_dev(pdev); if (!domain) return 0; - + iommu = domain_get_iommu(domain); size = aligned_size((u64)paddr, size); iova = __intel_alloc_iova(hwdev, domain, size, pdev->dma_mask); @@ -1849,7 +1876,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, * mappings.. */ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL || \ - !cap_zlr(domain->iommu->cap)) + !cap_zlr(iommu->cap)) prot |= DMA_PTE_READ; if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) prot |= DMA_PTE_WRITE; @@ -1865,10 +1892,10 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, goto error; /* it's a non-present to present mapping */ - ret = iommu_flush_iotlb_psi(domain->iommu, domain->id, + ret = iommu_flush_iotlb_psi(iommu, domain->id, start_paddr, size >> VTD_PAGE_SHIFT, 1); if (ret) - iommu_flush_write_buffer(domain->iommu); + iommu_flush_write_buffer(iommu); return start_paddr + ((u64)paddr & (~PAGE_MASK)); @@ -1897,7 +1924,7 @@ static void flush_unmaps(void) for (i = 0; i < g_num_of_iommus; i++) { if (deferred_flush[i].next) { struct intel_iommu *iommu = - deferred_flush[i].domain[0]->iommu; + deferred_flush[i].iommu; iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH, 0); @@ -1925,16 +1952,19 @@ static void add_unmap(struct dmar_domain *dom, struct iova *iova) { unsigned long flags; int next, iommu_id; + struct intel_iommu *iommu; spin_lock_irqsave(&async_umap_flush_lock, flags); if (list_size == HIGH_WATER_MARK) flush_unmaps(); - iommu_id = dom->iommu->seq_id; + iommu = domain_get_iommu(dom); + iommu_id = iommu->seq_id; next = deferred_flush[iommu_id].next; deferred_flush[iommu_id].domain[next] = dom; deferred_flush[iommu_id].iova[next] = iova; + deferred_flush[iommu_id].iommu = iommu; deferred_flush[iommu_id].next++; if (!timer_on) { @@ -1950,6 +1980,7 @@ void intel_unmap_single(struct device *dev, dma_addr_t dev_addr, size_t size, { struct pci_dev *pdev = to_pci_dev(dev); struct dmar_domain *domain; + struct intel_iommu *iommu; unsigned long start_addr; struct iova *iova; @@ -1958,6 +1989,8 @@ void intel_unmap_single(struct device *dev, dma_addr_t dev_addr, size_t size, domain = find_domain(pdev); BUG_ON(!domain); + iommu = domain_get_iommu(domain); + iova = find_iova(&domain->iovad, IOVA_PFN(dev_addr)); if (!iova) return; @@ -1973,9 +2006,9 @@ void intel_unmap_single(struct device *dev, dma_addr_t dev_addr, size_t size, /* free page tables */ dma_pte_free_pagetable(domain, start_addr, start_addr + size); if (intel_iommu_strict) { - if (iommu_flush_iotlb_psi(domain->iommu, + if (iommu_flush_iotlb_psi(iommu, domain->id, start_addr, size >> VTD_PAGE_SHIFT, 0)) - iommu_flush_write_buffer(domain->iommu); + iommu_flush_write_buffer(iommu); /* free iova */ __free_iova(&domain->iovad, iova); } else { @@ -2031,6 +2064,7 @@ void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist, int i; struct pci_dev *pdev = to_pci_dev(hwdev); struct dmar_domain *domain; + struct intel_iommu *iommu; unsigned long start_addr; struct iova *iova; size_t size = 0; @@ -2045,6 +2079,9 @@ void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist, iova = find_iova(&domain->iovad, IOVA_PFN(sglist[0].dma_address)); if (!iova) return; + + iommu = domain_get_iommu(domain); + for_each_sg(sglist, sg, nelems, i) { addr = SG_ENT_VIRT_ADDRESS(sg); size += aligned_size((u64)addr, sg->length); @@ -2057,9 +2094,9 @@ void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist, /* free page tables */ dma_pte_free_pagetable(domain, start_addr, start_addr + size); - if (iommu_flush_iotlb_psi(domain->iommu, domain->id, start_addr, + if (iommu_flush_iotlb_psi(iommu, domain->id, start_addr, size >> VTD_PAGE_SHIFT, 0)) - iommu_flush_write_buffer(domain->iommu); + iommu_flush_write_buffer(iommu); /* free iova */ __free_iova(&domain->iovad, iova); @@ -2086,6 +2123,7 @@ int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems, int i; struct pci_dev *pdev = to_pci_dev(hwdev); struct dmar_domain *domain; + struct intel_iommu *iommu; size_t size = 0; int prot = 0; size_t offset = 0; @@ -2102,6 +2140,8 @@ int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems, if (!domain) return 0; + iommu = domain_get_iommu(domain); + for_each_sg(sglist, sg, nelems, i) { addr = SG_ENT_VIRT_ADDRESS(sg); addr = (void *)virt_to_phys(addr); @@ -2119,7 +2159,7 @@ int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems, * mappings.. */ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL || \ - !cap_zlr(domain->iommu->cap)) + !cap_zlr(iommu->cap)) prot |= DMA_PTE_READ; if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) prot |= DMA_PTE_WRITE; @@ -2151,9 +2191,9 @@ int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int nelems, } /* it's a non-present to present mapping */ - if (iommu_flush_iotlb_psi(domain->iommu, domain->id, + if (iommu_flush_iotlb_psi(iommu, domain->id, start_addr, offset >> VTD_PAGE_SHIFT, 1)) - iommu_flush_write_buffer(domain->iommu); + iommu_flush_write_buffer(iommu); return nelems; } diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 952df39..66f7887 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -115,7 +115,7 @@ struct intel_iommu; struct dmar_domain { int id; /* domain id */ - struct intel_iommu *iommu; /* back pointer to owning iommu */ + unsigned long iommu_bmp; /* bitmap of iommus this domain uses*/ struct list_head devices; /* all devices' list */ struct iova_domain iovad; /* iova's that belong to this domain */ -- 1.5.1
0001-iommu-bitmap-insteads-of-iommu-pointer.patch
Description: 0001-iommu-bitmap-insteads-of-iommu-pointer.patch