Message ID | 1301035806.2402.470.camel@pasglop (mailing list archive) |
---|---|
State | Accepted, archived |
Delegated to: | Benjamin Herrenschmidt |
Headers | show |
At Fri, 25 Mar 2011 17:50:06 +1100, Benjamin Herrenschmidt wrote: > > 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 > > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> > --- > > 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(-) > > diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h > index 6d2416a..41e0eb7 100644 > --- a/arch/powerpc/include/asm/dma-mapping.h > +++ b/arch/powerpc/include/asm/dma-mapping.h > @@ -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) 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 > > #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) > { > diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c > index cf02cad..0f52235 100644 > --- a/arch/powerpc/kernel/dma.c > +++ b/arch/powerpc/kernel/dma.c > @@ -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); > +} > + > diff --git a/arch/powerpc/mm/dma-noncoherent.c b/arch/powerpc/mm/dma-noncoherent.c > index 757c0be..174b10a 100644 > --- a/arch/powerpc/mm/dma-noncoherent.c > +++ b/arch/powerpc/mm/dma-noncoherent.c > @@ -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); > +} > > >
On Fri, 2011-03-25 at 09:06 +0100, Takashi Iwai wrote: > > @@ -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. 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.
At Fri, 25 Mar 2011 20:11:57 +1100, Benjamin Herrenschmidt wrote: > > On Fri, 2011-03-25 at 09:06 +0100, Takashi Iwai wrote: > > > @@ -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. > > 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
On Fri, 2011-03-25 at 17:50 +1100, Benjamin Herrenschmidt wrote: > 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. > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> > --- > > 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(-) > > diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h > index 6d2416a..41e0eb7 100644 > --- a/arch/powerpc/include/asm/dma-mapping.h > +++ b/arch/powerpc/include/asm/dma-mapping.h > @@ -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) > { > diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c > index cf02cad..0f52235 100644 > --- a/arch/powerpc/kernel/dma.c > +++ b/arch/powerpc/kernel/dma.c > @@ -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); > +} > + > diff --git a/arch/powerpc/mm/dma-noncoherent.c b/arch/powerpc/mm/dma-noncoherent.c > index 757c0be..174b10a 100644 > --- a/arch/powerpc/mm/dma-noncoherent.c > +++ b/arch/powerpc/mm/dma-noncoherent.c > @@ -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 > Linuxppc-dev@lists.ozlabs.org > https://lists.ozlabs.org/listinfo/linuxppc-dev
diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h index 6d2416a..41e0eb7 100644 --- a/arch/powerpc/include/asm/dma-mapping.h +++ b/arch/powerpc/include/asm/dma-mapping.h @@ -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) { diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index cf02cad..0f52235 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -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); +} + diff --git a/arch/powerpc/mm/dma-noncoherent.c b/arch/powerpc/mm/dma-noncoherent.c index 757c0be..174b10a 100644 --- a/arch/powerpc/mm/dma-noncoherent.c +++ b/arch/powerpc/mm/dma-noncoherent.c @@ -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); +}
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 Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> --- 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(-)