diff options
| author | Omer Peleg <omer@cs.technion.ac.il> | 2016-04-20 04:33:57 -0400 |
|---|---|---|
| committer | David Woodhouse <David.Woodhouse@intel.com> | 2016-04-20 15:07:22 -0400 |
| commit | 2aac630429d986a43ac59525a4cff47a624dc58e (patch) | |
| tree | b007758a4aa3cf32fc5388037d3594ac169915ef | |
| parent | 0824c5920b16fe11034f3b5d2d48456d282d83f9 (diff) | |
iommu/vt-d: change intel-iommu to use IOVA frame numbers
Make intel-iommu map/unmap/invalidate work with IOVA pfns instead of
pointers to "struct iova". This avoids using the iova struct from the IOVA
red-black tree and the resulting explicit find_iova() on unmap.
This patch will allow us to cache IOVAs in the next patch, in order to
avoid rbtree operations for the majority of map/unmap operations.
Note: In eliminating the find_iova() operation, we have also eliminated
the sanity check previously done in the unmap flow. Arguably, this was
overhead that is better avoided in production code, but it could be
brought back as a debug option for driver development.
Signed-off-by: Omer Peleg <omer@cs.technion.ac.il>
[mad@cs.technion.ac.il: rebased, fixed to not break iova api, and reworded
the commit message]
Signed-off-by: Adam Morrison <mad@cs.technion.ac.il>
Reviewed-by: Shaohua Li <shli@fb.com>
Reviewed-by: Ben Serebrin <serebrin@google.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
| -rw-r--r-- | drivers/iommu/intel-iommu.c | 61 |
1 files changed, 29 insertions, 32 deletions
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index d100583e15e3..a8babc43e6d4 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c | |||
| @@ -459,7 +459,7 @@ static LIST_HEAD(dmar_rmrr_units); | |||
| 459 | static void flush_unmaps_timeout(unsigned long data); | 459 | static void flush_unmaps_timeout(unsigned long data); |
| 460 | 460 | ||
| 461 | struct deferred_flush_entry { | 461 | struct deferred_flush_entry { |
| 462 | struct iova *iova; | 462 | unsigned long iova_pfn; |
| 463 | unsigned long nrpages; | 463 | unsigned long nrpages; |
| 464 | struct dmar_domain *domain; | 464 | struct dmar_domain *domain; |
| 465 | struct page *freelist; | 465 | struct page *freelist; |
| @@ -3353,7 +3353,7 @@ error: | |||
| 3353 | } | 3353 | } |
| 3354 | 3354 | ||
| 3355 | /* This takes a number of _MM_ pages, not VTD pages */ | 3355 | /* This takes a number of _MM_ pages, not VTD pages */ |
| 3356 | static struct iova *intel_alloc_iova(struct device *dev, | 3356 | static unsigned long intel_alloc_iova(struct device *dev, |
| 3357 | struct dmar_domain *domain, | 3357 | struct dmar_domain *domain, |
| 3358 | unsigned long nrpages, uint64_t dma_mask) | 3358 | unsigned long nrpages, uint64_t dma_mask) |
| 3359 | { | 3359 | { |
| @@ -3373,16 +3373,16 @@ static struct iova *intel_alloc_iova(struct device *dev, | |||
| 3373 | iova = alloc_iova(&domain->iovad, nrpages, | 3373 | iova = alloc_iova(&domain->iovad, nrpages, |
| 3374 | IOVA_PFN(DMA_BIT_MASK(32)), 1); | 3374 | IOVA_PFN(DMA_BIT_MASK(32)), 1); |
| 3375 | if (iova) | 3375 | if (iova) |
| 3376 | return iova; | 3376 | return iova->pfn_lo; |
| 3377 | } | 3377 | } |
| 3378 | iova = alloc_iova(&domain->iovad, nrpages, IOVA_PFN(dma_mask), 1); | 3378 | iova = alloc_iova(&domain->iovad, nrpages, IOVA_PFN(dma_mask), 1); |
| 3379 | if (unlikely(!iova)) { | 3379 | if (unlikely(!iova)) { |
| 3380 | pr_err("Allocating %ld-page iova for %s failed", | 3380 | pr_err("Allocating %ld-page iova for %s failed", |
| 3381 | nrpages, dev_name(dev)); | 3381 | nrpages, dev_name(dev)); |
| 3382 | return NULL; | 3382 | return 0; |
| 3383 | } | 3383 | } |
| 3384 | 3384 | ||
| 3385 | return iova; | 3385 | return iova->pfn_lo; |
| 3386 | } | 3386 | } |
| 3387 | 3387 | ||
| 3388 | static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev) | 3388 | static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev) |
| @@ -3480,7 +3480,7 @@ static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr, | |||
| 3480 | { | 3480 | { |
| 3481 | struct dmar_domain *domain; | 3481 | struct dmar_domain *domain; |
| 3482 | phys_addr_t start_paddr; | 3482 | phys_addr_t start_paddr; |
| 3483 | struct iova *iova; | 3483 | unsigned long iova_pfn; |
| 3484 | int prot = 0; | 3484 | int prot = 0; |
| 3485 | int ret; | 3485 | int ret; |
| 3486 | struct intel_iommu *iommu; | 3486 | struct intel_iommu *iommu; |
| @@ -3498,8 +3498,8 @@ static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr, | |||
| 3498 | iommu = domain_get_iommu(domain); | 3498 | iommu = domain_get_iommu(domain); |
| 3499 | size = aligned_nrpages(paddr, size); | 3499 | size = aligned_nrpages(paddr, size); |
| 3500 | 3500 | ||
| 3501 | iova = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size), dma_mask); | 3501 | iova_pfn = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size), dma_mask); |
| 3502 | if (!iova) | 3502 | if (!iova_pfn) |
| 3503 | goto error; | 3503 | goto error; |
| 3504 | 3504 | ||
| 3505 | /* | 3505 | /* |
| @@ -3517,7 +3517,7 @@ static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr, | |||
| 3517 | * might have two guest_addr mapping to the same host paddr, but this | 3517 | * might have two guest_addr mapping to the same host paddr, but this |
| 3518 | * is not a big problem | 3518 | * is not a big problem |
| 3519 | */ | 3519 | */ |
| 3520 | ret = domain_pfn_mapping(domain, mm_to_dma_pfn(iova->pfn_lo), | 3520 | ret = domain_pfn_mapping(domain, mm_to_dma_pfn(iova_pfn), |
| 3521 | mm_to_dma_pfn(paddr_pfn), size, prot); | 3521 | mm_to_dma_pfn(paddr_pfn), size, prot); |
| 3522 | if (ret) | 3522 | if (ret) |
| 3523 | goto error; | 3523 | goto error; |
| @@ -3525,18 +3525,18 @@ static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr, | |||
| 3525 | /* it's a non-present to present mapping. Only flush if caching mode */ | 3525 | /* it's a non-present to present mapping. Only flush if caching mode */ |
| 3526 | if (cap_caching_mode(iommu->cap)) | 3526 | if (cap_caching_mode(iommu->cap)) |
| 3527 | iommu_flush_iotlb_psi(iommu, domain, | 3527 | iommu_flush_iotlb_psi(iommu, domain, |
| 3528 | mm_to_dma_pfn(iova->pfn_lo), | 3528 | mm_to_dma_pfn(iova_pfn), |
| 3529 | size, 0, 1); | 3529 | size, 0, 1); |
| 3530 | else | 3530 | else |
| 3531 | iommu_flush_write_buffer(iommu); | 3531 | iommu_flush_write_buffer(iommu); |
| 3532 | 3532 | ||
| 3533 | start_paddr = (phys_addr_t)iova->pfn_lo << PAGE_SHIFT; | 3533 | start_paddr = (phys_addr_t)iova_pfn << PAGE_SHIFT; |
| 3534 | start_paddr += paddr & ~PAGE_MASK; | 3534 | start_paddr += paddr & ~PAGE_MASK; |
| 3535 | return start_paddr; | 3535 | return start_paddr; |
| 3536 | 3536 | ||
| 3537 | error: | 3537 | error: |
| 3538 | if (iova) | 3538 | if (iova_pfn) |
| 3539 | __free_iova(&domain->iovad, iova); | 3539 | free_iova(&domain->iovad, iova_pfn); |
| 3540 | pr_err("Device %s request: %zx@%llx dir %d --- failed\n", | 3540 | pr_err("Device %s request: %zx@%llx dir %d --- failed\n", |
| 3541 | dev_name(dev), size, (unsigned long long)paddr, dir); | 3541 | dev_name(dev), size, (unsigned long long)paddr, dir); |
| 3542 | return 0; | 3542 | return 0; |
| @@ -3576,7 +3576,7 @@ static void flush_unmaps(struct deferred_flush_data *flush_data) | |||
| 3576 | unsigned long mask; | 3576 | unsigned long mask; |
| 3577 | struct deferred_flush_entry *entry = | 3577 | struct deferred_flush_entry *entry = |
| 3578 | &flush_table->entries[j]; | 3578 | &flush_table->entries[j]; |
| 3579 | struct iova *iova = entry->iova; | 3579 | unsigned long iova_pfn = entry->iova_pfn; |
| 3580 | unsigned long nrpages = entry->nrpages; | 3580 | unsigned long nrpages = entry->nrpages; |
| 3581 | struct dmar_domain *domain = entry->domain; | 3581 | struct dmar_domain *domain = entry->domain; |
| 3582 | struct page *freelist = entry->freelist; | 3582 | struct page *freelist = entry->freelist; |
| @@ -3584,14 +3584,14 @@ static void flush_unmaps(struct deferred_flush_data *flush_data) | |||
| 3584 | /* On real hardware multiple invalidations are expensive */ | 3584 | /* On real hardware multiple invalidations are expensive */ |
| 3585 | if (cap_caching_mode(iommu->cap)) | 3585 | if (cap_caching_mode(iommu->cap)) |
| 3586 | iommu_flush_iotlb_psi(iommu, domain, | 3586 | iommu_flush_iotlb_psi(iommu, domain, |
| 3587 | mm_to_dma_pfn(iova->pfn_lo), | 3587 | mm_to_dma_pfn(iova_pfn), |
| 3588 | nrpages, !freelist, 0); | 3588 | nrpages, !freelist, 0); |
| 3589 | else { | 3589 | else { |
| 3590 | mask = ilog2(nrpages); | 3590 | mask = ilog2(nrpages); |
| 3591 | iommu_flush_dev_iotlb(domain, | 3591 | iommu_flush_dev_iotlb(domain, |
| 3592 | (uint64_t)iova->pfn_lo << PAGE_SHIFT, mask); | 3592 | (uint64_t)iova_pfn << PAGE_SHIFT, mask); |
| 3593 | } | 3593 | } |
| 3594 | __free_iova(&domain->iovad, iova); | 3594 | free_iova(&domain->iovad, iova_pfn); |
| 3595 | if (freelist) | 3595 | if (freelist) |
| 3596 | dma_free_pagelist(freelist); | 3596 | dma_free_pagelist(freelist); |
| 3597 | } | 3597 | } |
| @@ -3611,7 +3611,7 @@ static void flush_unmaps_timeout(unsigned long cpuid) | |||
| 3611 | spin_unlock_irqrestore(&flush_data->lock, flags); | 3611 | spin_unlock_irqrestore(&flush_data->lock, flags); |
| 3612 | } | 3612 | } |
| 3613 | 3613 | ||
| 3614 | static void add_unmap(struct dmar_domain *dom, struct iova *iova, | 3614 | static void add_unmap(struct dmar_domain *dom, unsigned long iova_pfn, |
| 3615 | unsigned long nrpages, struct page *freelist) | 3615 | unsigned long nrpages, struct page *freelist) |
| 3616 | { | 3616 | { |
| 3617 | unsigned long flags; | 3617 | unsigned long flags; |
| @@ -3645,7 +3645,7 @@ static void add_unmap(struct dmar_domain *dom, struct iova *iova, | |||
| 3645 | 3645 | ||
| 3646 | entry = &flush_data->tables[iommu_id].entries[entry_id]; | 3646 | entry = &flush_data->tables[iommu_id].entries[entry_id]; |
| 3647 | entry->domain = dom; | 3647 | entry->domain = dom; |
| 3648 | entry->iova = iova; | 3648 | entry->iova_pfn = iova_pfn; |
| 3649 | entry->nrpages = nrpages; | 3649 | entry->nrpages = nrpages; |
| 3650 | entry->freelist = freelist; | 3650 | entry->freelist = freelist; |
| 3651 | 3651 | ||
| @@ -3664,7 +3664,7 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) | |||
| 3664 | struct dmar_domain *domain; | 3664 | struct dmar_domain *domain; |
| 3665 | unsigned long start_pfn, last_pfn; | 3665 | unsigned long start_pfn, last_pfn; |
| 3666 | unsigned long nrpages; | 3666 | unsigned long nrpages; |
| 3667 | struct iova *iova; | 3667 | unsigned long iova_pfn; |
| 3668 | struct intel_iommu *iommu; | 3668 | struct intel_iommu *iommu; |
| 3669 | struct page *freelist; | 3669 | struct page *freelist; |
| 3670 | 3670 | ||
| @@ -3676,13 +3676,10 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) | |||
| 3676 | 3676 | ||
| 3677 | iommu = domain_get_iommu(domain); | 3677 | iommu = domain_get_iommu(domain); |
| 3678 | 3678 | ||
| 3679 | iova = find_iova(&domain->iovad, IOVA_PFN(dev_addr)); | 3679 | iova_pfn = IOVA_PFN(dev_addr); |
| 3680 | if (WARN_ONCE(!iova, "Driver unmaps unmatched page at PFN %llx\n", | ||
| 3681 | (unsigned long long)dev_addr)) | ||
| 3682 | return; | ||
| 3683 | 3680 | ||
| 3684 | nrpages = aligned_nrpages(dev_addr, size); | 3681 | nrpages = aligned_nrpages(dev_addr, size); |
| 3685 | start_pfn = mm_to_dma_pfn(iova->pfn_lo); | 3682 | start_pfn = mm_to_dma_pfn(iova_pfn); |
| 3686 | last_pfn = start_pfn + nrpages - 1; | 3683 | last_pfn = start_pfn + nrpages - 1; |
| 3687 | 3684 | ||
| 3688 | pr_debug("Device %s unmapping: pfn %lx-%lx\n", | 3685 | pr_debug("Device %s unmapping: pfn %lx-%lx\n", |
| @@ -3694,10 +3691,10 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) | |||
| 3694 | iommu_flush_iotlb_psi(iommu, domain, start_pfn, | 3691 | iommu_flush_iotlb_psi(iommu, domain, start_pfn, |
| 3695 | nrpages, !freelist, 0); | 3692 | nrpages, !freelist, 0); |
| 3696 | /* free iova */ | 3693 | /* free iova */ |
| 3697 | __free_iova(&domain->iovad, iova); | 3694 | free_iova(&domain->iovad, iova_pfn); |
| 3698 | dma_free_pagelist(freelist); | 3695 | dma_free_pagelist(freelist); |
| 3699 | } else { | 3696 | } else { |
| 3700 | add_unmap(domain, iova, nrpages, freelist); | 3697 | add_unmap(domain, iova_pfn, nrpages, freelist); |
| 3701 | /* | 3698 | /* |
| 3702 | * queue up the release of the unmap to save the 1/6th of the | 3699 | * queue up the release of the unmap to save the 1/6th of the |
| 3703 | * cpu used up by the iotlb flush operation... | 3700 | * cpu used up by the iotlb flush operation... |
| @@ -3810,7 +3807,7 @@ static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nele | |||
| 3810 | struct dmar_domain *domain; | 3807 | struct dmar_domain *domain; |
| 3811 | size_t size = 0; | 3808 | size_t size = 0; |
| 3812 | int prot = 0; | 3809 | int prot = 0; |
| 3813 | struct iova *iova = NULL; | 3810 | unsigned long iova_pfn; |
| 3814 | int ret; | 3811 | int ret; |
| 3815 | struct scatterlist *sg; | 3812 | struct scatterlist *sg; |
| 3816 | unsigned long start_vpfn; | 3813 | unsigned long start_vpfn; |
| @@ -3829,9 +3826,9 @@ static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nele | |||
| 3829 | for_each_sg(sglist, sg, nelems, i) | 3826 | for_each_sg(sglist, sg, nelems, i) |
| 3830 | size += aligned_nrpages(sg->offset, sg->length); | 3827 | size += aligned_nrpages(sg->offset, sg->length); |
| 3831 | 3828 | ||
| 3832 | iova = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size), | 3829 | iova_pfn = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size), |
| 3833 | *dev->dma_mask); | 3830 | *dev->dma_mask); |
| 3834 | if (!iova) { | 3831 | if (!iova_pfn) { |
| 3835 | sglist->dma_length = 0; | 3832 | sglist->dma_length = 0; |
| 3836 | return 0; | 3833 | return 0; |
| 3837 | } | 3834 | } |
| @@ -3846,13 +3843,13 @@ static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nele | |||
| 3846 | if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) | 3843 | if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) |
| 3847 | prot |= DMA_PTE_WRITE; | 3844 | prot |= DMA_PTE_WRITE; |
| 3848 | 3845 | ||
| 3849 | start_vpfn = mm_to_dma_pfn(iova->pfn_lo); | 3846 | start_vpfn = mm_to_dma_pfn(iova_pfn); |
| 3850 | 3847 | ||
| 3851 | ret = domain_sg_mapping(domain, start_vpfn, sglist, size, prot); | 3848 | ret = domain_sg_mapping(domain, start_vpfn, sglist, size, prot); |
| 3852 | if (unlikely(ret)) { | 3849 | if (unlikely(ret)) { |
| 3853 | dma_pte_free_pagetable(domain, start_vpfn, | 3850 | dma_pte_free_pagetable(domain, start_vpfn, |
| 3854 | start_vpfn + size - 1); | 3851 | start_vpfn + size - 1); |
| 3855 | __free_iova(&domain->iovad, iova); | 3852 | free_iova(&domain->iovad, iova_pfn); |
| 3856 | return 0; | 3853 | return 0; |
| 3857 | } | 3854 | } |
| 3858 | 3855 | ||
