From 69abbee01268780b8ccb952555ce8362ad8a8b5e Mon Sep 17 00:00:00 2001 From: Pavel Golikov Date: Mon, 6 Jun 2022 17:57:53 +0300 Subject: [PATCH 1/2] ARM/dma-mapping: implement ->alloc_noncontiguous Implement support for allocating a non-contiguous DMA region. Implementation is based on one in ma-iommu driver. Signed-off-by: Pavel Golikov --- arch/arm/mm/dma-mapping.c | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 4b61541853ea..bbccfba968fc 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1757,6 +1757,63 @@ static void arm_iommu_unmap_sg(struct device *dev, __iommu_unmap_sg(dev, sg, nents, dir, attrs, false); } +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 @@ -1994,6 +2051,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, -- 2.25.1