From c9eb16e60f4a0b7c4ebd182f731cf94c6deeb738 Mon Sep 17 00:00:00 2001 From: Neeti Desai Date: Wed, 6 May 2015 15:38:34 -0700 Subject: [PATCH] arm64: dma-mapping: Split large pages when allocating in atomic context In atomic context, gen_pool_alloc allocates a single page large enough to accomodate the requested size. However __iommu_create_mapping always maps pages assuming they are of size 4K. Thus only the first 4K of the buffer is mapped and a translation fault is generated during an unmap. Fix this by splitting the larger pages into 4K pages. Change-Id: Ifcbe29477ad210204028486bd011470fe8b50852 Signed-off-by: Neeti Desai Signed-off-by: Mitchel Humpherys [pdaly@codeaurora.org Keep upstream version of alloc_from_pool] Signed-off-by: Patrick Daly --- arch/arm64/mm/dma-mapping.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 5701dd481d2c..72cc1fe2277a 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -1389,20 +1389,38 @@ static void *__iommu_alloc_atomic(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) { struct page *page; + struct page **pages; + int count = size >> PAGE_SHIFT; + int array_size = count * sizeof(struct page *); + int i; void *addr; + if (array_size <= PAGE_SIZE) + pages = kzalloc(array_size, gfp); + else + pages = vzalloc(array_size); + + if (!pages) + return NULL; + addr = __alloc_from_pool(size, &page, gfp); if (!addr) - return NULL; + goto err_free; - *handle = __iommu_create_mapping(dev, &page, size); + for (i = 0; i < count ; i++) + pages[i] = page + i; + + *handle = __iommu_create_mapping(dev, pages, size); if (*handle == DMA_ERROR_CODE) goto err_mapping; + kvfree(pages); return addr; err_mapping: __free_from_pool(addr, size); +err_free: + kvfree(pages); return NULL; }