diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 5409225b4abc..971022e45916 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1315,6 +1315,63 @@ static void arm_iommu_unmap_sg(struct device *dev, } } +static struct sg_table *arm_iommu_alloc_noncontiguous(struct device *dev, + size_t size, enum dma_data_direction dir, gfp_t gfp, + unsigned long attrs) +{ + struct dma_sgt_handle *sh; + int count; + + sh = kmalloc(sizeof(*sh), gfp); + if (!sh) + return NULL; + + size = PAGE_ALIGN(size); + count = size >> PAGE_SHIFT; + + /* + * Following is a work-around (a.k.a. hack) to prevent pages + * with __GFP_COMP being passed to split_page() which cannot + * handle them. The real problem is that this flag probably + * should be 0 on ARM as it is not supported on this + * platform; see CONFIG_HUGETLBFS. + */ + gfp &= ~(__GFP_COMP); + + sh->pages = __iommu_alloc_buffer(dev, size, gfp, attrs, false); + if (!sh->pages) + goto err_sh; + + if (sg_alloc_table_from_pages(&sh->sgt, sh->pages, count, 0, size, + GFP_KERNEL)) + goto err_buffer; + + if (__iommu_map_sg(dev, sh->sgt.sgl, sh->sgt.orig_nents, dir, attrs, + false) < 1) + goto err_free_sg; + + return &sh->sgt; + +err_free_sg: + sg_free_table(&sh->sgt); +err_buffer: + __iommu_free_buffer(dev, sh->pages, size, attrs); +err_sh: + kfree(sh); + return NULL; +} + +static void arm_iommu_free_noncontiguous(struct device *dev, size_t size, + struct sg_table *sgt, enum dma_data_direction dir) +{ + struct dma_sgt_handle *sh = sgt_handle(sgt); + + __iommu_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir, 0, false); + __iommu_free_buffer(dev, sh->pages, PAGE_ALIGN(size), 0); + sg_free_table(&sh->sgt); + kfree(sh); +} + /** * arm_iommu_sync_sg_for_cpu * @dev: valid struct device pointer @@ -1521,6 +1578,8 @@ static const struct dma_map_ops iommu_ops = { .map_page = arm_iommu_map_page, .unmap_page = arm_iommu_unmap_page, + .alloc_noncontiguous = arm_iommu_alloc_noncontiguous, + .free_noncontiguous = arm_iommu_free_noncontiguous, .sync_single_for_cpu = arm_iommu_sync_single_for_cpu, .sync_single_for_device = arm_iommu_sync_single_for_device,