arm64: IOMMU-backed DMA mapping
Subsystems (3)
Robin Murphy
maintainerv2 cover arm64: IOMMU-backed DMA mapping
Hi all,
Here's a quick repost of [1] with a couple of minor fixes:
- fix scatterlist dma_len for segments with nonzero offset
- adjust the bus notifier priority with a less silly value
The branch at [2] has been updated as well.
Robin.
[1]:http://thread.gmane.org/gmane.linux.kernel.iommu/9721 [2]:git://linux-arm.org/linux-rm iommu/dma
Robin Murphy (4):
iommu/iova: Avoid over-allocating when size-aligned
iommu: Implement common IOMMU ops for DMA mapping
arm64: Add IOMMU dma_ops
arm64: Hook up IOMMU dma_ops
arch/arm64/Kconfig | 1 + arch/arm64/include/asm/device.h | 3 + arch/arm64/include/asm/dma-mapping.h | 25 +- arch/arm64/mm/dma-mapping.c | 357 ++++++++++++++++++++++ drivers/iommu/Kconfig | 7 + drivers/iommu/Makefile | 1 + drivers/iommu/dma-iommu.c | 560 +++++++++++++++++++++++++++++++++++ drivers/iommu/intel-iommu.c | 2 + drivers/iommu/iova.c | 23 +- include/linux/dma-iommu.h | 94 ++++++ 10 files changed, 1051 insertions(+), 22 deletions(-) create mode 100644 drivers/iommu/dma-iommu.c create mode 100644 include/linux/dma-iommu.h
Signature · 1 line
1.9.1
Robin Murphy
maintainerPATCH v2 1/4 iommu/iova: Avoid over-allocating when size-aligned
Currently, allocating a size-aligned IOVA region quietly adjusts the actual allocation size in the process, returning a rounded-up power-of-two-sized allocation. This results in mismatched behaviour in the IOMMU driver if the original size was not a power of two, where the original size is mapped, but the rounded-up IOVA size is unmapped.
Whilst some IOMMUs will happily unmap already-unmapped pages, others consider this an error, so fix it by computing the necessary alignment padding without altering the actual allocation size. Also clean up by making pad_size unsigned, since negative padding makes no sense.
--- drivers/iommu/intel-iommu.c | 2 ++ drivers/iommu/iova.c | 23 ++++++----------------- 2 files changed, 8 insertions(+), 17 deletions(-)
drivers/iommu/intel-iommu.c +2 -0
@@ -2910,6 +2910,8 @@ static struct iova *intel_alloc_iova(struct device *dev,/* Restrict dma_mask to the width that the iommu can handle */ dma_mask =min_t (uint64_t,DOMAIN_MAX_ADDR (domain->gaw), dma_mask); +/* Ensure we reserve the whole size-aligned region */ + nrpages =__roundup_pow_of_two (nrpages);if (!dmar_forcedac && dma_mask >DMA_BIT_MASK (32 )) {/*
drivers/iommu/iova.c +6 -17
@@ -120,19 +120,14 @@ __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free) } } -/* Computes the padding size required, to make the -* the start address naturally aligned on its size +/* +* Computes the padding size required, to make the start address +* naturally aligned on the power-of-two order of its size */ -static int -iova_get_pad_size (int size,unsigned int limit_pfn) +static unsigned int +iova_get_pad_size (unsigned int size,unsigned int limit_pfn) { -unsigned int pad_size =0 ; -unsigned int order =ilog2 (size); - -if (order) - pad_size = (limit_pfn +1 ) % (1 << order); - -return pad_size; +return (limit_pfn +1 - size) & (__roundup_pow_of_two (size) -1 ); } static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
@@ -264,12 +259,6 @@ alloc_iova(struct iova_domain *iovad, unsigned long size,if (!new_iova)return NULL ; -/* If size aligned is set then round the size to -* to next power of two. -*/ -if (size_aligned) - size =__roundup_pow_of_two (size); - ret =__alloc_and_insert_iova_range (iovad, size, limit_pfn, new_iova, size_aligned);
Signature · 1 line
1.9.1
Robin Murphy
maintainerPATCH v2 2/4 iommu: Implement common IOMMU ops for DMA mapping
In reply to Robin MurphyTaking inspiration from the existing arch/arm code, break out some generic functions to interface the DMA-API to the IOMMU-API. This will do the bulk of the heavy lifting for IOMMU-backed dma-mapping.
--- drivers/iommu/Kconfig | 7 + drivers/iommu/Makefile | 1 + drivers/iommu/dma-iommu.c | 560 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 94 ++++++++ 4 files changed, 662 insertions(+) create mode 100644 drivers/iommu/dma-iommu.c create mode 100644 include/linux/dma-iommu.h
drivers/iommu/Kconfig +7 -0
@@ -48,6 +48,13 @@ config OF_IOMMU def_bool y depends on OF && IOMMU_API +# IOMMU-agnostic DMA-mapping layer +config IOMMU_DMA + bool + depends on NEED_SG_DMA_LENGTH + select IOMMU_API + select IOMMU_IOVA + config FSL_PAMU bool "Freescale IOMMU support" depends on PPC32
drivers/iommu/Makefile +1 -0
@@ -1,6 +1,7 @@ obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o +obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o obj-$(CONFIG_IOMMU_IOVA) += iova.o
drivers/iommu/dma-iommu.c new file +560 -0
560 lines not on this page · raw message
include/linux/dma-iommu.h new file +94 -0
94 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH v2 2/4 iommu: Implement common IOMMU ops for DMA mapping
Hi Robin,
while our drm test, we meet a problem while dma-mmap.
Quoted 14 lines · Robin Murphy
Taking inspiration from the existing
arch/armcode, break out some generic functions to interface the DMA-API to the IOMMU-API. This will do the bulk of the heavy lifting for IOMMU-backed dma-mapping.Signed-off-by Robin Murphy <[email protected]>--- drivers/iommu/Kconfig | 7 + drivers/iommu/Makefile | 1 + drivers/iommu/dma-iommu.c | 560 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 94 ++++++++ 4 files changed, 662 insertions(+) create mode 100644 drivers/iommu/dma-iommu.c create mode 100644 include/linux/dma-iommu.h
[snip]
Quoted 7 lines
drivers/iommu/dma-iommu.c new file +7
-0+/** +* iommu_dma_mmap - Map a buffer into provided user VMA +* @pages: Array representing buffer from iommu_dma_alloc() +* @size: Size of buffer in bytes +* @vma: VMA describing requested userspace mapping +* +* Maps the pages of the buffer in @pages into @vma. The caller is responsible
drivers/iommu/dma-iommu.c line 393
Quoted 10 lines
drivers/iommu/dma-iommu.c new file +10
-0+* for verifying the correct size and protection of @vma beforehand. +*/ + +int iommu_dma_mmap (struct page **pages,size_t size,struct vm_area_struct *vma) +{ +unsigned long uaddr = vma->vm_start; +unsigned int i, count =PAGE_ALIGN (size) >>PAGE_SHIFT ; +int ret = -ENXIO ; + +for (i = vma->vm_pgoff; i < count && uaddr < vma->vm_end; i++) {
Is it : for (i = 0; i < count && uaddr < vma->vm_end; i++)
Quoted 8 lines
drivers/iommu/dma-iommu.c new file +8
-0+ ret =vm_insert_page (vma, uaddr, pages[i]); +if (ret) +break ; + uaddr +=PAGE_SIZE ; + } +return ret; +} +
Robin Murphy
maintainerPATCH v2 2/4 iommu: Implement common IOMMU ops for DMA mapping
Quoted 29 lines · Yong Wu
Hi Robin,
while our drm test, we meet a problem while dma-mmap.Quoted 14 lines · Robin Murphy
Taking inspiration from the existing
arch/armcode, break out some generic functions to interface the DMA-API to the IOMMU-API. This will do the bulk of the heavy lifting for IOMMU-backed dma-mapping.Signed-off-by Robin Murphy <[email protected]>--- drivers/iommu/Kconfig | 7 + drivers/iommu/Makefile | 1 + drivers/iommu/dma-iommu.c | 560 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 94 ++++++++ 4 files changed, 662 insertions(+)create mode 100644
drivers/iommu/dma-iommu.c
create mode 100644include/linux/dma-iommu.h[snip]
Quoted 10 lines
drivers/iommu/dma-iommu.c new file +10
-0+/** +* iommu_dma_mmap - Map a buffer into provided user VMA +* @pages: Array representing buffer from iommu_dma_alloc() +* @size: Size of buffer in bytes +* @vma: VMA describing requested userspace mapping +* +* Maps the pages of the buffer in @pages into @vma. The caller is responsible +* for verifying the correct size and protection of @vma beforehand. +*/ +
Quoted 8 lines
drivers/iommu/dma-iommu.c line 393
Quoted 7 lines
drivers/iommu/dma-iommu.c new file +7
-0+int iommu_dma_mmap (struct page **pages,size_t size,struct vm_area_struct *vma) +{ +unsigned long uaddr = vma->vm_start; +unsigned int i, count =PAGE_ALIGN (size) >>PAGE_SHIFT ; +int ret = -ENXIO ; + +for (i = vma->vm_pgoff; i < count && uaddr < vma->vm_end; i++) {Is it : for (i = 0; i < count && uaddr <
vma->vm_end; i++)
According to the patches at [1], respecting vma->pgoff is necessary for users who only want to mmap part of the buffer. Does your DRM driver by any chance need the same fix as the Rockchip one?
Quoted 8 lines
drivers/iommu/dma-iommu.c line 401
Quoted 8 lines
drivers/iommu/dma-iommu.c new file +8
-0+ ret =vm_insert_page (vma, uaddr, pages[i]); +if (ret) +break ; + uaddr +=PAGE_SIZE ; + } +return ret; +} +
And to save another email:
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ if (WARN_ON(!area || area->pages))Is it: if (
WARN_ON(!area || !area->pages))Review
Quoted 1 line
(quoted diff) +1
-0+ return -ENXIO;
You're absolutely right, thanks. I've got a couple more bugfixes in progress too, so I'll try to get an updated branch pushed out soon; I'll hold off posting a v3 to the list until after the upcoming merge window, though.
Robin.
PATCH v2 2/4 iommu: Implement common IOMMU ops for DMA mapping
Quoted 39 lines · Robin Murphy
Quoted 29 lines · Yong Wu
Hi Robin,
while our drm test, we meet a problem while dma-mmap.Quoted 14 lines · Robin Murphy
Taking inspiration from the existing
arch/armcode, break out some generic functions to interface the DMA-API to the IOMMU-API. This will do the bulk of the heavy lifting for IOMMU-backed dma-mapping.Signed-off-by Robin Murphy <[email protected]>--- drivers/iommu/Kconfig | 7 + drivers/iommu/Makefile | 1 + drivers/iommu/dma-iommu.c | 560 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 94 ++++++++ 4 files changed, 662 insertions(+)create mode 100644
drivers/iommu/dma-iommu.c
create mode 100644include/linux/dma-iommu.h[snip]
Quoted 10 lines
drivers/iommu/dma-iommu.c new file +10
-0+/** +* iommu_dma_mmap - Map a buffer into provided user VMA +* @pages: Array representing buffer from iommu_dma_alloc() +* @size: Size of buffer in bytes +* @vma: VMA describing requested userspace mapping +* +* Maps the pages of the buffer in @pages into @vma. The caller is responsible +* for verifying the correct size and protection of @vma beforehand. +*/ +Quoted 8 lines
drivers/iommu/dma-iommu.c line 393
Quoted 7 lines
drivers/iommu/dma-iommu.c new file +7
-0+int iommu_dma_mmap (struct page **pages,size_t size,struct vm_area_struct *vma) +{ +unsigned long uaddr = vma->vm_start; +unsigned int i, count =PAGE_ALIGN (size) >>PAGE_SHIFT ; +int ret = -ENXIO ; + +for (i = vma->vm_pgoff; i < count && uaddr < vma->vm_end; i++) {Is it : for (i = 0; i < count && uaddr <
vma->vm_end; i++)
Quoted 3 lines
According to the patches at [1], respecting
vma->pgoffis necessary for users who only want to mmap part of the buffer. Does your DRM driver by any chance need the same fix as the Rockchip one?
Thanks. We will try to add it.
Quoted 25 lines
Quoted 9 lines
drivers/iommu/dma-iommu.c line 401
Quoted 9 lines
drivers/iommu/dma-iommu.c new file +8
-0+ ret =vm_insert_page (vma, uaddr, pages[i]); +if (ret) +break ; + uaddr +=PAGE_SIZE ; + } +return ret; +} +And to save another email:
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ if (WARN_ON(!area || area->pages))Is it: if (
WARN_ON(!area || !area->pages))Review
Quoted 1 line
(quoted diff) +1
-0+ return -ENXIO;You're absolutely right, thanks. I've got a couple more bugfixes in progress too, so I'll try to get an updated branch pushed out soon; I'll hold off posting a v3 to the list until after the upcoming merge window, though.
Robin.
PATCH v2 2/4 iommu: Implement common IOMMU ops for DMA mapping
In reply to Robin MurphyQuoted 44 lines · Robin Murphy
Taking inspiration from the existing
arch/armcode, break out some generic functions to interface the DMA-API to the IOMMU-API. This will do the bulk of the heavy lifting for IOMMU-backed dma-mapping.Signed-off-by Robin Murphy <[email protected]>--- drivers/iommu/Kconfig | 7 + drivers/iommu/Makefile | 1 + drivers/iommu/dma-iommu.c | 560 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 94 ++++++++ 4 files changed, 662 insertions(+) create mode 100644 drivers/iommu/dma-iommu.c create mode 100644 include/linux/dma-iommu.hdrivers/iommu/Kconfig +7
-0@@ -48,6 +48,13 @@ config OF_IOMMU def_bool y depends on OF && IOMMU_API +# IOMMU-agnostic DMA-mapping layer +config IOMMU_DMA + bool + depends on NEED_SG_DMA_LENGTH + select IOMMU_API + select IOMMU_IOVA + config FSL_PAMU bool "Freescale IOMMU support" depends on PPC32drivers/iommu/Makefile +1
-0@@ -1,6 +1,7 @@ obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o +obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o obj-$(CONFIG_IOMMU_IOVA) += iova.o
[snip]
Quoted 24 lines
drivers/iommu/Makefile +24
-0+ +/** + * iommu_dma_alloc - Allocate and map a buffer contiguous in IOVA space + * @dev: Device to allocate memory for. Must be a real device + * attached to an iommu_dma_domain + * @size: Size of buffer in bytes + * @gfp: Allocation flags + * @prot: IOMMU mapping flags + * @coherent: Which dma_mask to base IOVA allocation on + * @handle: Out argument for allocated DMA handle + * @flush_page: Arch callback to flush a single page from caches as + * necessary. May be NULL for coherent allocations + * + * If @size is less than PAGE_SIZE, then a full CPU page will be allocated, + * but an IOMMU which supports smaller pages might not map the whole thing. + * For now, the buffer is unconditionally zeroed for compatibility + * + * Return: Array of struct page pointers describing the buffer, + * or NULL on failure. + */ +struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp, + int prot, bool coherent, dma_addr_t *handle, + void (*flush_page)(const void *, phys_addr_t)) +{
drivers/iommu/Makefile
Quoted 10 lines
drivers/iommu/Makefile +10
-0+ struct iommu_dma_domain *dom = arch_get_dma_domain(dev); + struct iova_domain *iovad = dom->iovad; + struct iova *iova; + struct page **pages; + struct sg_table sgt; + struct sg_mapping_iter miter; + dma_addr_t dma_addr; + unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; + + *handle = DMA_ERROR_CODE;
Hi Robin,
Compare with the dma before, You delete this line here.
size = PAGE_ALIGN(size);
Do you expect the user should make sure the size must be aligned?
so do __iommu_free_attrs.
Quoted 41 lines
drivers/iommu/Makefile +41
-0+ + /* IOMMU can map any pages, so himem can also be used here */ + gfp |= __GFP_NOWARN | __GFP_HIGHMEM; + pages = __iommu_dma_alloc_pages(count, gfp); + if (!pages) + return NULL; + + iova = __alloc_iova(dev, size, coherent); + if (!iova) + goto out_free_pages; + + if (sg_alloc_table_from_pages(&sgt, pages, count, 0, size, GFP_KERNEL)) + goto out_free_iova; + + dma_addr = iova_dma_addr(iovad, iova); + if (iommu_map_sg(dom->domain, dma_addr, sgt.sgl, sgt.orig_nents, prot) + < size) + goto out_free_sg; + + /* Using the non-flushing flag since we're doing our own */ + sg_miter_start(&miter, sgt.sgl, sgt.orig_nents, SG_MITER_FROM_SG); + while (sg_miter_next(&miter)) { + memset(miter.addr, 0, PAGE_SIZE); + if (flush_page) + flush_page(miter.addr, page_to_phys(miter.page)); + } + sg_miter_stop(&miter); + sg_free_table(&sgt); + + *handle = dma_addr; + return pages; + +out_free_sg: + sg_free_table(&sgt); +out_free_iova: + __free_iova(iovad, iova); +out_free_pages: + __iommu_dma_free_pages(pages, count); + return NULL; +} +
[...]
Quoted 3 lines
drivers/iommu/Makefile +3
-0+ +#endif /* __KERNEL__ */ +#endif /* __DMA_IOMMU_H */
Robin Murphy
maintainerPATCH v2 2/4 iommu: Implement common IOMMU ops for DMA mapping
On 03/07/15 10:27, Yong Wu wrote:
[...]
Quoted 36 lines
Quoted 23 lines
drivers/iommu/dma-iommu.c new file +23
-0+/** +* iommu_dma_alloc - Allocate and map a buffer contiguous in IOVA space +* @dev: Device to allocate memory for. Must be a real device +* attached to an iommu_dma_domain +* @size: Size of buffer in bytes +* @gfp: Allocation flags +* @prot: IOMMU mapping flags +* @coherent: Which dma_mask to base IOVA allocation on +* @handle: Out argument for allocated DMA handle +* @flush_page: Arch callback to flush a single page from caches as +* necessary. May be NULL for coherent allocations +* +* If @size is less than PAGE_SIZE, then a full CPU page will be allocated, +* but an IOMMU which supports smaller pages might not map the whole thing. +* For now, the buffer is unconditionally zeroed for compatibility +* +* Return: Array of struct page pointers describing the buffer, +* or NULL on failure. +*/ +struct page **iommu_dma_alloc (struct device *dev,size_t size,gfp_t gfp, +int prot,bool coherent,dma_addr_t *handle, +void (*flush_page)(const void *,phys_addr_t )) +{drivers/iommu/dma-iommu.c line 335
Quoted 10 lines
drivers/iommu/dma-iommu.c new file +10
-0+struct iommu_dma_domain *dom =arch_get_dma_domain (dev); +struct iova_domain *iovad = dom->iovad; +struct iova *iova; +struct page **pages; +struct sg_table sgt; +struct sg_mapping_iter miter; +dma_addr_t dma_addr; +unsigned int count =PAGE_ALIGN (size) >>PAGE_SHIFT ; + + *handle =DMA_ERROR_CODE ;Hi Robin,
Quoted 7 lines
Compare with the dma before, You delete this line here.
size = PAGE_ALIGN(size);Do you expect the user should make sure the size must be aligned?
so do
__iommu_free_attrs.
Yeah, there is an oversight here due to some back-and-forth refactoring
- both the page allocation and the IOVA allocation do get suitably aligned as they should, but the segments of the temporary scatterlist don't. Somehow I managed not to hit an iommu_map_sg failure due to that until some time after this posting (it seems most of the drivers I test with only make page-sized allocations...)
I've currently got the fixup below waiting for the next posting.
Robin.
Quoted 11 lines
drivers/iommu/dma-iommu.c line 346
Quoted 11 lines
drivers/iommu/dma-iommu.c new file +11
-0+ +/* IOMMU can map any pages, so himem can also be used here */ + gfp |= __GFP_NOWARN | __GFP_HIGHMEM; + pages =__iommu_dma_alloc_pages (count, gfp); +if (!pages) +return NULL ; + + iova =__alloc_iova (dev, size, coherent); +if (!iova) + goto out_free_pages; +
+ size = iova_align(iovad, size);
Quoted 36 lines
Quoted 30 lines
drivers/iommu/dma-iommu.c new file +30
-0+if (sg_alloc_table_from_pages (&sgt, pages, count,0 , size,GFP_KERNEL )) + goto out_free_iova; + + dma_addr =iova_dma_addr (iovad, iova); +if (iommu_map_sg (dom->domain, dma_addr, sgt.sgl, sgt.orig_nents, prot) + < size) + goto out_free_sg; + +/* Using the non-flushing flag since we're doing our own */ +sg_miter_start (&miter, sgt.sgl, sgt.orig_nents,SG_MITER_FROM_SG ); +while (sg_miter_next (&miter)) { +memset (miter.addr,0 ,PAGE_SIZE ); +if (flush_page) +flush_page (miter.addr,page_to_phys (miter.page)); + } +sg_miter_stop (&miter); +sg_free_table (&sgt); + + *handle = dma_addr; +return pages; + +out_free_sg: +sg_free_table (&sgt); +out_free_iova: +__free_iova (iovad, iova); +out_free_pages: +__iommu_dma_free_pages (pages, count); +return NULL ; +} +[...]
Quoted 3 lines
include/linux/dma-iommu.h new file +3
-0+ +#endif /* __KERNEL__ */ +#endif /* __DMA_IOMMU_H */
PATCH v2 2/4 iommu: Implement common IOMMU ops for DMA mapping
Quoted 53 lines · Robin Murphy
On 03/07/15 10:27, Yong Wu wrote:
[...]Quoted 36 lines
Quoted 23 lines
drivers/iommu/dma-iommu.c new file +23
-0+/** +* iommu_dma_alloc - Allocate and map a buffer contiguous in IOVA space +* @dev: Device to allocate memory for. Must be a real device +* attached to an iommu_dma_domain +* @size: Size of buffer in bytes +* @gfp: Allocation flags +* @prot: IOMMU mapping flags +* @coherent: Which dma_mask to base IOVA allocation on +* @handle: Out argument for allocated DMA handle +* @flush_page: Arch callback to flush a single page from caches as +* necessary. May be NULL for coherent allocations +* +* If @size is less than PAGE_SIZE, then a full CPU page will be allocated, +* but an IOMMU which supports smaller pages might not map the whole thing. +* For now, the buffer is unconditionally zeroed for compatibility +* +* Return: Array of struct page pointers describing the buffer, +* or NULL on failure. +*/ +struct page **iommu_dma_alloc (struct device *dev,size_t size,gfp_t gfp, +int prot,bool coherent,dma_addr_t *handle, +void (*flush_page)(const void *,phys_addr_t )) +{drivers/iommu/dma-iommu.c line 335
Quoted 10 lines
drivers/iommu/dma-iommu.c new file +10
-0+struct iommu_dma_domain *dom =arch_get_dma_domain (dev); +struct iova_domain *iovad = dom->iovad; +struct iova *iova; +struct page **pages; +struct sg_table sgt; +struct sg_mapping_iter miter; +dma_addr_t dma_addr; +unsigned int count =PAGE_ALIGN (size) >>PAGE_SHIFT ; + + *handle =DMA_ERROR_CODE ;Hi Robin,
Quoted 7 lines
Compare with the dma before, You delete this line here.
size = PAGE_ALIGN(size);Do you expect the user should make sure the size must be aligned?
so do
__iommu_free_attrs.Yeah, there is an oversight here due to some back-and-forth refactoring
- both the page allocation and the IOVA allocation do get suitably aligned as they should, but the segments of the temporary scatterlist don't. Somehow I managed not to hit aniommu_map_sgfailure due to that until some time after this posting (it seems most of the drivers I test with only make page-sized allocations...)
Quoted 3 lines
I've currently got the fixup below waiting for the next posting.
Robin.
Hi Robin,
Could we know the status and ETA of the DMA next version. We are preparing the mtk-iommu next patch as our project request. We could help test it if you send the new patch. Thanks.
Quoted 52 lines
Quoted 12 lines
Quoted 12 lines
drivers/iommu/dma-iommu.c new file +11
-0+ +/* IOMMU can map any pages, so himem can also be used here */ + gfp |= __GFP_NOWARN | __GFP_HIGHMEM; + pages =__iommu_dma_alloc_pages (count, gfp); +if (!pages) +return NULL ; + + iova =__alloc_iova (dev, size, coherent); +if (!iova) + goto out_free_pages; +(quoted diff) +1
-0+ size = iova_align(iovad, size);Quoted 37 lines
Quoted 31 lines
drivers/iommu/dma-iommu.c new file +30
-0+if (sg_alloc_table_from_pages (&sgt, pages, count,0 , size,GFP_KERNEL )) + goto out_free_iova; + + dma_addr =iova_dma_addr (iovad, iova); +if (iommu_map_sg (dom->domain, dma_addr, sgt.sgl, sgt.orig_nents, prot) + < size) + goto out_free_sg; + +/* Using the non-flushing flag since we're doing our own */ +sg_miter_start (&miter, sgt.sgl, sgt.orig_nents,SG_MITER_FROM_SG ); +while (sg_miter_next (&miter)) { +memset (miter.addr,0 ,PAGE_SIZE ); +if (flush_page) +flush_page (miter.addr,page_to_phys (miter.page)); + } +sg_miter_stop (&miter); +sg_free_table (&sgt); + + *handle = dma_addr; +return pages; + +out_free_sg: +sg_free_table (&sgt); +out_free_iova: +__free_iova (iovad, iova); +out_free_pages: +__iommu_dma_free_pages (pages, count); +return NULL ; +} +[...]
Quoted 3 lines
include/linux/dma-iommu.h new file +3
-0+ +#endif /* __KERNEL__ */ +#endif /* __DMA_IOMMU_H */
Robin Murphy
maintainerPATCH v2 3/4 arm64: Add IOMMU dma_ops
In reply to Robin MurphyTaking some inspiration from the arch/arm code, implement the arch-specific side of the DMA mapping ops using the new IOMMU-DMA layer.
Whilst proliferating per-device private IOMMU data via dev->archdata is less than ideal, it will do the job for now, especially since we can't easily handle the kind of problematic system topologies in the current IOMMU API anyway.
--- arch/arm64/include/asm/device.h | 3 + arch/arm64/include/asm/dma-mapping.h | 14 ++ arch/arm64/mm/dma-mapping.c | 342 +++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 4 +- 4 files changed, 361 insertions(+), 2 deletions(-)
arch/arm64/include/asm/device.h +3 -0
@@ -20,6 +20,9 @@ struct dev_archdata {struct dma_map_ops *dma_ops;#ifdef CONFIG_IOMMU_API void *iommu;/* private IOMMU data */ +#ifdef CONFIG_IOMMU_DMA +struct iommu_dma_domain *dma_domain; +#endif #endif bool dma_coherent; };
arch/arm64/include/asm/dma-mapping.h +14 -0
@@ -61,6 +61,20 @@ static inline bool is_device_dma_coherent(struct device *dev) }#include <asm-generic/dma-mapping-common.h> +#include <linux/dma-iommu.h> + +#ifdef CONFIG_IOMMU_DMA +static inline struct iommu_dma_domain *arch_get_dma_domain (struct device *dev) +{ +return dev->archdata.dma_domain; +} + +static inline void arch_set_dma_domain (struct device *dev, +struct iommu_dma_domain *dma_domain) +{ + dev->archdata.dma_domain = dma_domain; +} +#endif static inline dma_addr_t phys_to_dma (struct device *dev,phys_addr_t paddr) {
arch/arm64/mm/dma-mapping.c +342 -0
345 lines not on this page · raw message
include/linux/dma-iommu.h +2 -2
@@ -79,8 +79,6 @@ static inline int iommu_dma_init(void)return 0 ; } -#endif /* CONFIG_IOMMU_DMA */ -static inline struct iommu_dma_domain *arch_get_dma_domain (struct device *dev) {return NULL ;
@@ -90,5 +88,7 @@ static inline void arch_set_dma_domain(struct device *dev, struct iommu_dma_domain *dma_domain) { } +#endif /* CONFIG_IOMMU_DMA */ +#endif /* __KERNEL__ */ #endif /* __DMA_IOMMU_H */
Signature · 1 line
1.9.1
PATCH v2 3/4 arm64: Add IOMMU dma_ops
Quoted 15 lines · Robin Murphy
Taking some inspiration from the
arch/armcode, implement the arch-specific side of the DMA mapping ops using the new IOMMU-DMA layer.Whilst proliferating per-device private IOMMU data via
dev->archdatais less than ideal, it will do the job for now, especially since we can't easily handle the kind of problematic system topologies in the current IOMMU API anyway.Signed-off-by Robin Murphy <[email protected]>--- arch/arm64/include/asm/device.h | 3 + arch/arm64/include/asm/dma-mapping.h | 14 ++ arch/arm64/mm/dma-mapping.c | 342 +++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 4 +- 4 files changed, 361 insertions(+), 2 deletions(-)
[snip]
Quoted 5 lines
arch/arm64/mm/dma-mapping.c +5
-0+static int __iommu_mmap_attrs (struct device *dev,struct vm_area_struct *vma, +void *cpu_addr,dma_addr_t dma_addr,size_t size, +struct dma_attrs *attrs) +{ +struct vm_struct *area;
arch/arm64/mm/dma-mapping.c static int __init dma_debug_do_init(void) · line 523
Quoted 10 lines
arch/arm64/mm/dma-mapping.c +10
-0+int ret; + + vma->vm_page_prot =__get_dma_pgprot (attrs, vma->vm_page_prot, +is_device_dma_coherent (dev)); + +if (dma_mmap_from_coherent (dev, vma, cpu_addr, size, &ret)) +return ret; + + area =find_vm_area (cpu_addr); +if (WARN_ON (!area || area->pages))
Is it: if (WARN_ON(!area || !area->pages))
Quoted 4 lines
arch/arm64/mm/dma-mapping.c +4
-0+return -ENXIO ; + +return iommu_dma_mmap (area->pages, size, vma); +}
[snip]
Quoted 2 lines
#endif /*
__KERNEL__*/
#endif /*__DMA_IOMMU_H*/
Robin Murphy
maintainerPATCH v2 4/4 arm64: Hook up IOMMU dma_ops
In reply to Robin MurphyWith iommu_dma_ops in place, hook them up to the configuration code, so IOMMU-fronted devices will get them automatically.
--- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/dma-mapping.h | 11 ++++++----- arch/arm64/mm/dma-mapping.c | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-)
arch/arm64/Kconfig +1 -0
@@ -70,6 +70,7 @@ config ARM64 select HAVE_PERF_USER_STACK_DUMP select HAVE_RCU_TABLE_FREE select HAVE_SYSCALL_TRACEPOINTS + select IOMMU_DMA if IOMMU_SUPPORT select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM
arch/arm64/include/asm/dma-mapping.h +6 -5
@@ -45,11 +45,8 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev)return __generic_dma_ops (dev); } -static inline void arch_setup_dma_ops (struct device *dev,u64 dma_base,u64 size, -struct iommu_ops *iommu,bool coherent) -{ - dev->archdata.dma_coherent = coherent; -} +void arch_setup_dma_ops (struct device *dev,u64 dma_base,u64 size, +struct iommu_ops *iommu,bool coherent);#define arch_setup_dma_ops arch_setup_dma_ops/* do not use this function in a driver */
@@ -64,6 +61,10 @@ static inline bool is_device_dma_coherent(struct device *dev)#include <linux/dma-iommu.h> #ifdef CONFIG_IOMMU_DMA + +void arch_teardown_dma_ops (struct device *dev); +#define arch_teardown_dma_ops arch_teardown_dma_ops +static inline struct iommu_dma_domain *arch_get_dma_domain (struct device *dev) {return dev->archdata.dma_domain;
arch/arm64/mm/dma-mapping.c +15 -0
@@ -769,6 +769,14 @@ static void __iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,mutex_unlock (&iommu_dma_notifier_lock); } +void arch_teardown_dma_ops (struct device *dev) +{ +if (dev->archdata.dma_domain) { +iommu_dma_detach_device (dev); + dev->archdata.dma_ops =NULL ; + } +} +#else static void __iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
@@ -776,3 +784,10 @@ static void __iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, { }#endif /* CONFIG_IOMMU_DMA */ + +void arch_setup_dma_ops (struct device *dev,u64 dma_base,u64 size, +struct iommu_ops *iommu,bool coherent) +{ + dev->archdata.dma_coherent = coherent; +__iommu_setup_dma_ops (dev, dma_base, size, iommu); +}
Signature · 1 line
1.9.1