This patch provides reliable mechanism for obtaining pages associated with a given dma_mapping. This is a proof-of-concept patch.
Signed-off-by: Tomasz Stanislawski t.stanislaws@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/include/asm/dma-mapping.h | 8 ++++++ arch/arm/mm/dma-mapping.c | 44 ++++++++++++++++++++++++++++++++++++ include/linux/dma-mapping.h | 2 + 3 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index ca7a378..79b6c3d 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -196,6 +196,14 @@ static inline int dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma, return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs); }
+static inline int dma_get_pages(struct device *dev, void *cpu_addr, + dma_addr_t dma_addr, struct page **pages, size_t n_pages) +{ + const struct dma_map_ops *ops = get_dma_ops(dev); + BUG_ON(!ops); + return ops->get_pages(dev, cpu_addr, dma_addr, pages, n_pages); +} + static inline void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag) { diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 2287b01..93a3508 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -116,10 +116,14 @@ static void arm_dma_sync_single_for_device(struct device *dev,
static int arm_dma_set_mask(struct device *dev, u64 dma_mask);
+static int arm_dma_get_pages(struct device *dev, void *cpu_addr, + dma_addr_t dma_addr, struct page **pages, size_t n_pages); + struct dma_map_ops arm_dma_ops = { .alloc = arm_dma_alloc, .free = arm_dma_free, .mmap = arm_dma_mmap, + .get_pages = arm_dma_get_pages, .map_page = arm_dma_map_page, .unmap_page = arm_dma_unmap_page, .map_sg = arm_dma_map_sg, @@ -531,6 +535,25 @@ int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma, }
/* + * Get pages for the DMA-coherent memory. + */ +static int arm_dma_get_pages(struct device *dev, void *cpu_addr, + dma_addr_t dma_addr, struct page **pages, size_t n_pages) +{ +#ifdef CONFIG_MMU + int i; + unsigned long pfn = dma_to_pfn(dev, dma_addr); + + for (i = 0; i < n_pages; ++i) + pages[i] = pfn_to_page(pfn + i); + + return n_pages; +#else + return -ENXIO; +#endif /* CONFIG_MMU */ +} + +/* * free a page as defined by the above mapping. * Must not be called with IRQs disabled. */ @@ -1033,6 +1056,26 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, return 0; }
+static int arm_iommu_get_pages(struct device *dev, void *cpu_addr, + dma_addr_t dma_addr, struct page **pages, size_t n_pages) +{ + struct arm_vmregion *c; + int n_valid_pages; + + c = arm_vmregion_find(&consistent_head, (unsigned long)cpu_addr); + + if (!c) + return -ENXIO; + + n_valid_pages = (c->vm_end - c->vm_start) >> PAGE_SHIFT; + if (n_valid_pages < n_pages) + n_pages = n_valid_pages; + + memcpy(pages, c->priv, n_pages * sizeof pages[0]); + + return n_pages; +} + /* * free a page as defined by the above mapping. * Must not be called with IRQs disabled. @@ -1271,6 +1314,7 @@ struct dma_map_ops iommu_ops = { .alloc = arm_iommu_alloc_attrs, .free = arm_iommu_free_attrs, .mmap = arm_iommu_mmap_attrs, + .get_pages = arm_iommu_get_pages,
.map_page = arm_iommu_map_page, .unmap_page = arm_iommu_unmap_page, diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index b903a20..409d3a9 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -17,6 +17,8 @@ struct dma_map_ops { struct dma_attrs *attrs); int (*mmap)(struct device *, struct vm_area_struct *, void *, dma_addr_t, size_t, struct dma_attrs *attrs); + int (*get_pages)(struct device *dev, void *vaddr, dma_addr_t dma_addr, + struct page **pages, size_t n_pages);
dma_addr_t (*map_page)(struct device *dev, struct page *page, unsigned long offset, size_t size,