Skip to contentSkip to conversation

powerpc: Implement dma_mmap_coherent()

PATCH In review Benjamin Herrenschmidt 2 participants 5 messages Mar 25, 2011 – Mar 29, 2011 PowerPC

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 powerpc: Implement dma_mmap_coherent()

This is used by Alsa to mmap buffers allocated with dma_alloc_coherent() into userspace. We need a special variant to handle machines with non-coherent DMAs as those buffers have "special" virt addresses and require non-cachable mappings

---

Dunno if anybody with CONFIG_NOT_COHERENT_CACHE has some audio device that uses dma buffers (ie not usb-audio) and wants to try that out... should fix a long standing problem.

 arch/powerpc/include/asm/dma-mapping.h |    7 +++++++
 arch/powerpc/kernel/dma.c              |   18 ++++++++++++++++++
 arch/powerpc/mm/dma-noncoherent.c      |   20 ++++++++++++++++++++
 3 files changed, 45 insertions(+), 0 deletions(-)
arch/powerpc/include/asm/dma-mapping.h +7 -0
index 6d2416a..41e0eb7 100644
@@ -42,6 +42,7 @@ extern void __dma_free_coherent(size_t size, void *vaddr);
 extern void __dma_sync(void *vaddr, size_t size, int direction);
 extern void __dma_sync_page(struct page *page, unsigned long offset,
 				 size_t size, int direction);
+extern unsigned long __dma_get_coherent_pfn(void *cpu_addr);
 
 #else /* ! CONFIG_NOT_COHERENT_CACHE */
 /*
@@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page, unsigned long offset,
 #define __dma_free_coherent(size, addr)		((void)0)
 #define __dma_sync(addr, size, rw)		((void)0)
 #define __dma_sync_page(pg, off, sz, rw)	((void)0)
+#define __dma_get_coherent_pfn(cpu_addr)	(0)
 
 #endif /* ! CONFIG_NOT_COHERENT_CACHE */
 
@@ -198,6 +200,11 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
 
+extern int dma_mmap_coherent(struct device *, struct vm_area_struct *,
+			     void *, dma_addr_t, size_t);
+#define ARCH_HAS_DMA_MMAP_COHERENT
+
+
 static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
 		enum dma_data_direction direction)
 {
arch/powerpc/kernel/dma.c +18 -0
index cf02cad..0f52235 100644
@@ -179,3 +179,21 @@ static int __init dma_init(void)
        return 0;
 }
 fs_initcall(dma_init);
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn;
+
+#ifdef CONFIG_NOT_COHERENT_CACHE
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	pfn = __dma_get_coherent_pfn(cpu_addr);
+#else
+	pfn = page_to_pfn(virt_to_page(cpu_addr));
+#endif
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
arch/powerpc/mm/dma-noncoherent.c +20 -0
index 757c0be..174b10a 100644
@@ -399,3 +399,23 @@ void __dma_sync_page(struct page *page, unsigned long offset,
 #endif
 }
 EXPORT_SYMBOL(__dma_sync_page);
+
+/*
+ * Return the PFN for a given cpu virtual address returned by
+ * __dma_alloc_coherent. This is used by dma_mmap_coherent()
+ */
+unsigned long __dma_get_coherent_pfn(void *cpu_addr)
+{	
+	/* This should always be populated, so we don't test every
+	 * level. If that fails, we'll have a nice crash which
+	 * will be as good as a BUG_ON()
+	 */
+	pgd_t *pgd = pgd_offset_k(cpu_addr);
+	pud_t *pud = pud_offset(pgd, cpu_addr);
+	pmd_t *pmd = pmd_offset(pud, cpu_addr);
+	pte_t *ptep = pte_offset_kernel(pmd, cpu_addr);
+
+	if (pte_none(*ptep) || !pte_present(*ptep))
+		return 0;
+	return pte_pfn(*ptep);	
+}
#1 lore raw

At Fri, 25 Mar 2011 17:50:06 +1100,

Quoted 31 lines · Benjamin Herrenschmidt

This is used by Alsa to mmap buffers allocated with dma_alloc_coherent() into userspace. We need a special variant to handle machines with non-coherent DMAs as those buffers have "special" virt addresses and require non-cachable mappings

---

Dunno if anybody with CONFIG_NOT_COHERENT_CACHE has some audio device that uses dma buffers (ie not usb-audio) and wants to try that out... should fix a long standing problem.

 arch/powerpc/include/asm/dma-mapping.h |    7 +++++++
 arch/powerpc/kernel/dma.c              |   18 ++++++++++++++++++
 arch/powerpc/mm/dma-noncoherent.c      |   20 ++++++++++++++++++++
 3 files changed, 45 insertions(+), 0 deletions(-)
arch/powerpc/include/asm/dma-mapping.h +1 -0
index 6d2416a..41e0eb7 100644
@@ -42,6 +42,7 @@ extern void __dma_free_coherent(size_t size, void *vaddr);
 extern void __dma_sync(void *vaddr, size_t size, int direction);
 extern void __dma_sync_page(struct page *page, unsigned long offset,
 				 size_t size, int direction);
+extern unsigned long __dma_get_coherent_pfn(void *cpu_addr);
 
 #else /* ! CONFIG_NOT_COHERENT_CACHE */
 /*

arch/powerpc/include/asm/dma-mapping.h extern void __dma_sync_page(struct page *page, unsigned long offset, · line 56

Quoted 4 lines
arch/powerpc/include/asm/dma-mapping.h +1 -0
@@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page, unsigned long offset,
 #define __dma_free_coherent(size, addr)		((void)0)
 #define __dma_sync(addr, size, rw)		((void)0)
 #define __dma_sync_page(pg, off, sz, rw)	((void)0)
+#define __dma_get_coherent_pfn(cpu_addr)	(0)

Just nitpicking: shouldn't we define some real thing like

	page_to_pfn(virt_to_page(cpu_addr))

or put something to trigger an error? Setting a bogus zero can be easily overlooked, I'm afraid.

thanks,

Takashi

Quoted 72 lines
arch/powerpc/include/asm/dma-mapping.h +5 -0
 #endif /* ! CONFIG_NOT_COHERENT_CACHE */
 
@@ -198,6 +200,11 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
 
+extern int dma_mmap_coherent(struct device *, struct vm_area_struct *,
+			     void *, dma_addr_t, size_t);
+#define ARCH_HAS_DMA_MMAP_COHERENT
+
+
 static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
 		enum dma_data_direction direction)
 {
arch/powerpc/kernel/dma.c +18 -0
index cf02cad..0f52235 100644
@@ -179,3 +179,21 @@ static int __init dma_init(void)
        return 0;
 }
 fs_initcall(dma_init);
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn;
+
+#ifdef CONFIG_NOT_COHERENT_CACHE
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	pfn = __dma_get_coherent_pfn(cpu_addr);
+#else
+	pfn = page_to_pfn(virt_to_page(cpu_addr));
+#endif
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
arch/powerpc/mm/dma-noncoherent.c +20 -0
index 757c0be..174b10a 100644
@@ -399,3 +399,23 @@ void __dma_sync_page(struct page *page, unsigned long offset,
 #endif
 }
 EXPORT_SYMBOL(__dma_sync_page);
+
+/*
+ * Return the PFN for a given cpu virtual address returned by
+ * __dma_alloc_coherent. This is used by dma_mmap_coherent()
+ */
+unsigned long __dma_get_coherent_pfn(void *cpu_addr)
+{	
+	/* This should always be populated, so we don't test every
+	 * level. If that fails, we'll have a nice crash which
+	 * will be as good as a BUG_ON()
+	 */
+	pgd_t *pgd = pgd_offset_k(cpu_addr);
+	pud_t *pud = pud_offset(pgd, cpu_addr);
+	pmd_t *pmd = pmd_offset(pud, cpu_addr);
+	pte_t *ptep = pte_offset_kernel(pmd, cpu_addr);
+
+	if (pte_none(*ptep) || !pte_present(*ptep))
+		return 0;
+	return pte_pfn(*ptep);	
+}
#2 lore raw
Quoted 11 lines · Takashi Iwai

Review extern void __dma_sync_page(struct page *page,

Quoted 1 line
(quoted diff) +0 -0
@@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page,

unsigned long offset,

arch/powerpc/include/asm/dma-mapping.h extern void __dma_sync_page(struct page *page, unsigned long offset, · line 56

Quoted 4 lines
arch/powerpc/include/asm/dma-mapping.h +1 -0
 #define __dma_free_coherent(size, addr)              ((void)0)
 #define __dma_sync(addr, size, rw)           ((void)0)
 #define __dma_sync_page(pg, off, sz, rw)     ((void)0)
+#define __dma_get_coherent_pfn(cpu_addr)     (0)

Just nitpicking: shouldn't we define some real thing like

        page_to_pfn(virt_to_page(cpu_addr))

or put something to trigger an error? Setting a bogus zero can be easily overlooked, I'm afraid.

Yes, all those variants are bogus if you noticed :-) The trick is that they aren't actually called when CONFIG_NOT_COHERENT_CACHE is not set anyways, with the exception of __dma_sync(), so I want to just remove them all. But I'll do that in a separate patch.

Cheers,
Ben.

#3 lore raw

At Fri, 25 Mar 2011 20:11:57 +1100,

Quoted 14 lines · Benjamin Herrenschmidt
Quoted 11 lines · Takashi Iwai

Review extern void __dma_sync_page(struct page *page,

Quoted 1 line
(quoted diff) +0 -0
@@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page,

unsigned long offset,

arch/powerpc/include/asm/dma-mapping.h extern void __dma_sync_page(struct page *page, unsigned long offset, · line 56

Quoted 4 lines
arch/powerpc/include/asm/dma-mapping.h +1 -0
 #define __dma_free_coherent(size, addr)              ((void)0)
 #define __dma_sync(addr, size, rw)           ((void)0)
 #define __dma_sync_page(pg, off, sz, rw)     ((void)0)
+#define __dma_get_coherent_pfn(cpu_addr)     (0)

Just nitpicking: shouldn't we define some real thing like

        page_to_pfn(virt_to_page(cpu_addr))

or put something to trigger an error? Setting a bogus zero can be easily overlooked, I'm afraid.

Quoted 4 lines

Yes, all those variants are bogus if you noticed :-) The trick is that they aren't actually called when CONFIG_NOT_COHERENT_CACHE is not set anyways, with the exception of __dma_sync(), so I want to just remove them all. But I'll do that in a separate patch.

OK, that makes sense.

thanks,

Takashi

#4 lore raw
In reply to Benjamin Herrenschmidt
Quoted 4 lines · Benjamin Herrenschmidt

This is used by Alsa to mmap buffers allocated with dma_alloc_coherent() into userspace. We need a special variant to handle machines with non-coherent DMAs as those buffers have "special" virt addresses and require non-cachable mappings

Missing an EXPORT_SYMBOL_GPL... will fix that up before I merge.

Cheers,
Ben.

Quoted 105 lines

---

Dunno if anybody with CONFIG_NOT_COHERENT_CACHE has some audio device that uses dma buffers (ie not usb-audio) and wants to try that out... should fix a long standing problem.

 arch/powerpc/include/asm/dma-mapping.h |    7 +++++++
 arch/powerpc/kernel/dma.c              |   18 ++++++++++++++++++
 arch/powerpc/mm/dma-noncoherent.c      |   20 ++++++++++++++++++++
 3 files changed, 45 insertions(+), 0 deletions(-)
arch/powerpc/include/asm/dma-mapping.h +7 -0
index 6d2416a..41e0eb7 100644
@@ -42,6 +42,7 @@ extern void __dma_free_coherent(size_t size, void *vaddr);
 extern void __dma_sync(void *vaddr, size_t size, int direction);
 extern void __dma_sync_page(struct page *page, unsigned long offset,
 				 size_t size, int direction);
+extern unsigned long __dma_get_coherent_pfn(void *cpu_addr);
 
 #else /* ! CONFIG_NOT_COHERENT_CACHE */
 /*
@@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page, unsigned long offset,
 #define __dma_free_coherent(size, addr)		((void)0)
 #define __dma_sync(addr, size, rw)		((void)0)
 #define __dma_sync_page(pg, off, sz, rw)	((void)0)
+#define __dma_get_coherent_pfn(cpu_addr)	(0)
 
 #endif /* ! CONFIG_NOT_COHERENT_CACHE */
 
@@ -198,6 +200,11 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
 
+extern int dma_mmap_coherent(struct device *, struct vm_area_struct *,
+			     void *, dma_addr_t, size_t);
+#define ARCH_HAS_DMA_MMAP_COHERENT
+
+
 static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
 		enum dma_data_direction direction)
 {
arch/powerpc/kernel/dma.c +18 -0
index cf02cad..0f52235 100644
@@ -179,3 +179,21 @@ static int __init dma_init(void)
        return 0;
 }
 fs_initcall(dma_init);
+
+int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
+		      void *cpu_addr, dma_addr_t handle, size_t size)
+{
+	unsigned long pfn;
+
+#ifdef CONFIG_NOT_COHERENT_CACHE
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	pfn = __dma_get_coherent_pfn(cpu_addr);
+#else
+	pfn = page_to_pfn(virt_to_page(cpu_addr));
+#endif
+	return remap_pfn_range(vma, vma->vm_start,
+			       pfn + vma->vm_pgoff,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
arch/powerpc/mm/dma-noncoherent.c +20 -0
index 757c0be..174b10a 100644
@@ -399,3 +399,23 @@ void __dma_sync_page(struct page *page, unsigned long offset,
 #endif
 }
 EXPORT_SYMBOL(__dma_sync_page);
+
+/*
+ * Return the PFN for a given cpu virtual address returned by
+ * __dma_alloc_coherent. This is used by dma_mmap_coherent()
+ */
+unsigned long __dma_get_coherent_pfn(void *cpu_addr)
+{	
+	/* This should always be populated, so we don't test every
+	 * level. If that fails, we'll have a nice crash which
+	 * will be as good as a BUG_ON()
+	 */
+	pgd_t *pgd = pgd_offset_k(cpu_addr);
+	pud_t *pud = pud_offset(pgd, cpu_addr);
+	pmd_t *pmd = pmd_offset(pud, cpu_addr);
+	pte_t *ptep = pte_offset_kernel(pmd, cpu_addr);
+
+	if (pte_none(*ptep) || !pte_present(*ptep))
+		return 0;
+	return pte_pfn(*ptep);	
+}

_______________________________________________
Linuxppc-dev mailing list
[email protected]
https://lists.ozlabs.org/listinfo/linuxppc-dev