iommu: dma-mapping-fast: Respect device coherency in IOMMU mapper
Coherent devices can make use of the CPU cache, so they should get coherent IOMMU mappings and should have their cache maintenance operations skipped. Implement this for the "fast" arm64 IOMMU mapper. Change-Id: I30db64cae2cc4cda0baa3cdd3860dc079f22bf71 Signed-off-by: Mitchel Humpherys <mitchelh@codeaurora.org>
This commit is contained in:
parent
5f701acda4
commit
486496dc03
1 changed files with 36 additions and 8 deletions
|
@ -25,6 +25,29 @@
|
|||
#define FAST_PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
#define FAST_PTE_ADDR_MASK ((av8l_fast_iopte)0xfffffffff000)
|
||||
|
||||
static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
|
||||
bool coherent)
|
||||
{
|
||||
if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs))
|
||||
return pgprot_noncached(prot);
|
||||
else if (!coherent || dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
|
||||
return pgprot_writecombine(prot);
|
||||
return prot;
|
||||
}
|
||||
|
||||
static int __get_iommu_pgprot(struct dma_attrs *attrs, int prot,
|
||||
bool coherent)
|
||||
{
|
||||
if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs))
|
||||
prot |= IOMMU_NOEXEC;
|
||||
if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs))
|
||||
prot |= IOMMU_DEVICE;
|
||||
if (coherent)
|
||||
prot |= IOMMU_CACHE;
|
||||
|
||||
return prot;
|
||||
}
|
||||
|
||||
static void fast_dmac_clean_range(struct dma_fast_smmu_mapping *mapping,
|
||||
void *start, void *end)
|
||||
{
|
||||
|
@ -289,11 +312,11 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page,
|
|||
int nptes = len >> FAST_PAGE_SHIFT;
|
||||
bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs);
|
||||
int prot = __fast_dma_direction_to_prot(dir);
|
||||
bool is_coherent = is_device_dma_coherent(dev);
|
||||
|
||||
if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs))
|
||||
prot |= IOMMU_DEVICE;
|
||||
prot = __get_iommu_pgprot(attrs, prot, is_coherent);
|
||||
|
||||
if (!skip_sync)
|
||||
if (!skip_sync && !is_coherent)
|
||||
__fast_dma_page_cpu_to_dev(phys_to_page(phys_to_map),
|
||||
offset_from_phys_to_map, size, dir);
|
||||
|
||||
|
@ -333,8 +356,9 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova,
|
|||
int nptes = len >> FAST_PAGE_SHIFT;
|
||||
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
|
||||
bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs);
|
||||
bool is_coherent = is_device_dma_coherent(dev);
|
||||
|
||||
if (!skip_sync)
|
||||
if (!skip_sync && !is_coherent)
|
||||
__fast_dma_page_dev_to_cpu(page, offset, size, dir);
|
||||
|
||||
spin_lock_irqsave(&mapping->lock, flags);
|
||||
|
@ -442,9 +466,12 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
|
|||
struct sg_mapping_iter miter;
|
||||
unsigned int count = ALIGN(size, SZ_4K) >> PAGE_SHIFT;
|
||||
int prot = IOMMU_READ | IOMMU_WRITE; /* TODO: extract from attrs */
|
||||
pgprot_t remap_prot = pgprot_writecombine(PAGE_KERNEL);
|
||||
bool is_coherent = is_device_dma_coherent(dev);
|
||||
pgprot_t remap_prot = __get_dma_pgprot(attrs, PAGE_KERNEL, is_coherent);
|
||||
struct page **pages;
|
||||
|
||||
prot = __get_iommu_pgprot(attrs, prot, is_coherent);
|
||||
|
||||
*handle = DMA_ERROR_CODE;
|
||||
|
||||
pages = __fast_smmu_alloc_pages(count, gfp);
|
||||
|
@ -459,7 +486,7 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
|
|||
goto out_free_pages;
|
||||
}
|
||||
|
||||
if (!(prot & IOMMU_CACHE)) {
|
||||
if (!is_coherent) {
|
||||
/*
|
||||
* The CPU-centric flushing implied by SG_MITER_TO_SG isn't
|
||||
* sufficient here, so skip it by using the "wrong" direction.
|
||||
|
@ -562,9 +589,10 @@ static int fast_smmu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
|
|||
unsigned long uaddr = vma->vm_start;
|
||||
struct page **pages;
|
||||
int i, nr_pages, ret = 0;
|
||||
bool coherent = is_device_dma_coherent(dev);
|
||||
|
||||
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
||||
|
||||
vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot,
|
||||
coherent);
|
||||
area = find_vm_area(cpu_addr);
|
||||
if (!area)
|
||||
return -EINVAL;
|
||||
|
|
Loading…
Add table
Reference in a new issue