sh: declared coherent memory support
PATCH 1/1 sh: declared coherent memory support
sh: declared coherent memory support
This patch adds declared coherent memory support to the sh architecture. All functions are copied straight from the x86 implementation and the header files are adjusted to use the newfunctions instead of the former consistent_alloc() code.
---
arch/sh/mm/consistent.c | 174 ++++++++++++++++++++++++++++++------------ include/asm-sh/dma-mapping.h | 56 ++++--------- 2 files changed, 147 insertions(+), 83 deletions(-)
arch/sh/mm/consistent.c +127 -47
@@ -3,6 +3,8 @@* * Copyright (C) 2004 - 2007 Paul Mundt * +* Declared coherent memory functions taken from arch/x86/kernel/pci-dma_32.c +* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details.
@@ -13,66 +15,144 @@#include <asm/addrspace.h> #include <asm/io.h> -void *consistent_alloc (gfp_t gfp,size_t size,dma_addr_t *handle) +struct dma_coherent_mem { +void *virt_base; +u32 device_base; +int size; +int flags; +unsigned long *bitmap; +}; + +void *dma_alloc_coherent (struct device *dev,size_t size, +dma_addr_t *dma_handle,gfp_t gfp) { -struct page *page, *end, *free; -void *ret, *vp; -int order; - - size =PAGE_ALIGN (size); - order =get_order (size); - - page =alloc_pages (gfp, order); -if (!page) -return NULL ; -split_page (page, order); - - ret =page_address (page); - *handle =virt_to_phys (ret); - - vp =ioremap_nocache (*handle, size); -if (!vp) { -free_pages ((unsigned long )ret, order); -return NULL ; +void *ret; +struct dma_coherent_mem *mem = dev ? dev->dma_mem :NULL ; +int order =get_order (size); +/* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + +if (mem) { +int page =bitmap_find_free_region (mem->bitmap, mem->size, + order); +if (page >=0 ) { + *dma_handle = mem->device_base + (page <<PAGE_SHIFT ); + ret = mem->virt_base + (page <<PAGE_SHIFT ); +memset (ret,0 , size); +return ret; + } +if (mem->flags &DMA_MEMORY_EXCLUSIVE ) +return NULL ; } -memset (vp,0 , size); +if (dev =NULL || (dev->coherent_dma_mask <0xffffffff )) + gfp |=GFP_DMA ; -/* -* We must flush the cache before we pass it on to the device -*/ -dma_cache_sync (NULL , ret, size,DMA_BIDIRECTIONAL ); - - page =virt_to_page (ret); - free = page + (size >>PAGE_SHIFT ); - end = page + (1 << order); - -while (++page < end) { -/* Free any unused pages */ -if (page >= free) { -__free_page (page); - } + ret = (void *)__get_free_pages (gfp, order); + +if (ret !=NULL ) { +memset (ret,0 , size); + *dma_handle =virt_to_phys (ret); } +return ret; +} +EXPORT_SYMBOL (dma_alloc_coherent); -return vp; +void dma_free_coherent (struct device *dev,size_t size, +void *vaddr,dma_addr_t dma_handle) +{ +struct dma_coherent_mem *mem = dev ? dev->dma_mem :NULL ; +int order =get_order (size); + +WARN_ON (irqs_disabled ());/* for portability */ +if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size <<PAGE_SHIFT ))) { +int page = (vaddr - mem->virt_base) >>PAGE_SHIFT ; + +bitmap_release_region (mem->bitmap, page, order); + }else +free_pages ((unsigned long )vaddr, order); } -EXPORT_SYMBOL (consistent_alloc); +EXPORT_SYMBOL (dma_free_coherent); -void consistent_free (void *vaddr,size_t size,dma_addr_t dma_handle) +int dma_declare_coherent_memory (struct device *dev,dma_addr_t bus_addr, +dma_addr_t device_addr,size_t size,int flags) { -struct page *page; -unsigned long addr; +void __iomem *mem_base =NULL ; +int pages = size >>PAGE_SHIFT ; +int bitmap_size =BITS_TO_LONGS (pages) *sizeof (long ); + +if ((flags & (DMA_MEMORY_MAP |DMA_MEMORY_IO )) =0 ) + goto out; +if (!size) + goto out; +if (dev->dma_mem) + goto out; + +/* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ + + mem_base =ioremap (bus_addr, size); +if (!mem_base) + goto out; + + dev->dma_mem =kzalloc (sizeof (struct dma_coherent_mem ),GFP_KERNEL ); +if (!dev->dma_mem) + goto out; + dev->dma_mem->bitmap =kzalloc (bitmap_size,GFP_KERNEL ); +if (!dev->dma_mem->bitmap) + goto free1_out; + + dev->dma_mem->virt_base = mem_base; + dev->dma_mem->device_base = device_addr; + dev->dma_mem->size = pages; + dev->dma_mem->flags = flags; + +if (flags &DMA_MEMORY_MAP ) +return DMA_MEMORY_MAP ; + +return DMA_MEMORY_IO ; + + free1_out: +kfree (dev->dma_mem); + out: +if (mem_base) +iounmap (mem_base); +return 0 ; +} +EXPORT_SYMBOL (dma_declare_coherent_memory); - addr = (unsigned long )phys_to_virt ((unsigned long )dma_handle); - page =virt_to_page (addr); +void dma_release_declared_memory (struct device *dev) +{ +struct dma_coherent_mem *mem = dev->dma_mem; -free_pages (addr,get_order (size)); +if (!mem) +return ; + dev->dma_mem =NULL ; +iounmap (mem->virt_base); +kfree (mem->bitmap); +kfree (mem); +} +EXPORT_SYMBOL (dma_release_declared_memory); -iounmap (vaddr); +void *dma_mark_declared_memory_occupied (struct device *dev, +dma_addr_t device_addr,size_t size) +{ +struct dma_coherent_mem *mem = dev->dma_mem; +int pages = (size + (device_addr & ~PAGE_MASK ) +PAGE_SIZE -1 ) >>PAGE_SHIFT ; +int pos, err; + +if (!mem) +return ERR_PTR (-EINVAL ); + + pos = (device_addr - mem->device_base) >>PAGE_SHIFT ; + err =bitmap_allocate_region (mem->bitmap, pos,get_order (pages)); +if (err !=0 ) +return ERR_PTR (err); +return mem->virt_base + (pos <<PAGE_SHIFT ); } -EXPORT_SYMBOL (consistent_free); +EXPORT_SYMBOL (dma_mark_declared_memory_occupied); -void consistent_sync (void *vaddr,size_t size,int direction) +void dma_cache_sync (struct device *dev,void *vaddr,size_t size, +enum dma_data_direction direction) {#ifdef CONFIG_CPU_SH5 void *p1addr = vaddr;
@@ -94,4 +174,4 @@ void consistent_sync(void *vaddr, size_tBUG (); } } -EXPORT_SYMBOL (consistent_sync); +EXPORT_SYMBOL (dma_cache_sync);
include/asm-sh/dma-mapping.h +20 -36
79 lines not on this page · raw message
Quoted 5 lines · Magnus Damm
This patch adds declared coherent memory support to the sh architecture. All functions are copied straight from the x86 implementation and the header files are adjusted to use the newfunctions instead of the former
consistent_alloc()code.
[snip]
arch/sh/mm/consistent.c line 49
Quoted 4 lines
arch/sh/mm/consistent.c +2
-1-memset (vp,0 , size); +if (dev =NULL || (dev->coherent_dma_mask <0xffffffff )) + gfp |=GFP_DMA ;
This is obviously not something we care about, we don't have a ZONE_DMA, and GFP_DMA has no meaning here. The page allocator will obviously still do the right thing, but it's a bit ambiguous to suggest that we have some sort of bogus limitation. The coherent_dma_mask is basically unintersting in the general case, there are two situations in which we care about it, and those are > 32-bits physical, or a restricted address space. In the latter case we have to bounce it anyways, so a bogus DMA zone isn't going to make a difference there. Likewise, in the > 32-bits physical case, we also just don't care.
arch/sh/mm/consistent.c line 59
Quoted 10 lines
arch/sh/mm/consistent.c +8
-0+ ret = (void *)__get_free_pages (gfp, order); + +if (ret !=NULL ) { +memset (ret,0 , size); + *dma_handle =virt_to_phys (ret); } +return ret; +} +EXPORT_SYMBOL (dma_alloc_coherent);
I do wonder whether it's sane to forego the dma_cache_sync() in the allocation path in this case. If the allocation is satisfied by the bitmap, it can probably be skipped, but as far as data we're fetching and remapping from the page allocator, I suspect this is still going to need a sync.
arch/sh/mm/consistent.c line 96
Quoted 4 lines
arch/sh/mm/consistent.c +4
-0+ mem_base =ioremap (bus_addr, size); +if (!mem_base) + goto out; +
This should be ioremap_nocache().
arch/sh/mm/consistent.c line 99
Quoted 3 lines
arch/sh/mm/consistent.c +3
-0+ dev->dma_mem =kzalloc (sizeof (struct dma_coherent_mem ),GFP_KERNEL ); +if (!dev->dma_mem) + goto out;
This can probably be kmalloc(), since everything is being initialized anyways, no point in zeroing it out.
arch/sh/mm/consistent.c line 133
Quoted 12 lines
arch/sh/mm/consistent.c +10
-1+void dma_release_declared_memory (struct device *dev) +{ +struct dma_coherent_mem *mem = dev->dma_mem; -free_pages (addr,get_order (size)); +if (!mem) +return ; + dev->dma_mem =NULL ; +iounmap (mem->virt_base); +kfree (mem->bitmap); +kfree (mem); +}
What happens if someone is still using the bitmap?
PATCH 1/1 sh: declared coherent memory support V2
In reply to Magnus Dammsh: declared coherent memory support V2
This patch adds declared coherent memory support to the sh architecture. All functions are based on the x86 implementation. Header files are adjusted to use the new functions instead of the former consistent_alloc() code.
This version includes the few changes what were included in the fix patch together with modifications based on feedback from Paul.
---
arch/sh/mm/consistent.c | 176 ++++++++++++++++++++++++++++++------------ include/asm-sh/dma-mapping.h | 56 ++++--------- 2 files changed, 149 insertions(+), 83 deletions(-)
arch/sh/mm/consistent.c +129 -47
@@ -3,6 +3,8 @@* * Copyright (C) 2004 - 2007 Paul Mundt * +* Declared coherent memory functions based on arch/x86/kernel/pci-dma_32.c +* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details.
@@ -13,66 +15,146 @@#include <asm/addrspace.h> #include <asm/io.h> -void *consistent_alloc (gfp_t gfp,size_t size,dma_addr_t *handle) +struct dma_coherent_mem { +void *virt_base; +u32 device_base; +int size; +int flags; +unsigned long *bitmap; +}; + +void *dma_alloc_coherent (struct device *dev,size_t size, +dma_addr_t *dma_handle,gfp_t gfp) { -struct page *page, *end, *free; -void *ret, *vp; -int order; - - size =PAGE_ALIGN (size); - order =get_order (size); - - page =alloc_pages (gfp, order); -if (!page) -return NULL ; -split_page (page, order); - - ret =page_address (page); - *handle =virt_to_phys (ret); - - vp =ioremap_nocache (*handle, size); -if (!vp) { -free_pages ((unsigned long )ret, order); -return NULL ; +void *ret; +struct dma_coherent_mem *mem = dev ? dev->dma_mem :NULL ; +int order =get_order (size); + +if (mem) { +int page =bitmap_find_free_region (mem->bitmap, mem->size, + order); +if (page >=0 ) { + *dma_handle = mem->device_base + (page <<PAGE_SHIFT ); + ret = mem->virt_base + (page <<PAGE_SHIFT ); +memset (ret,0 , size); +return ret; + } +if (mem->flags &DMA_MEMORY_EXCLUSIVE ) +return NULL ; } -memset (vp,0 , size); + ret = (void *)__get_free_pages (gfp, order); -/* -* We must flush the cache before we pass it on to the device -*/ -dma_cache_sync (NULL , ret, size,DMA_BIDIRECTIONAL ); - - page =virt_to_page (ret); - free = page + (size >>PAGE_SHIFT ); - end = page + (1 << order); - -while (++page < end) { -/* Free any unused pages */ -if (page >= free) { -__free_page (page); - } +if (ret !=NULL ) { +memset (ret,0 , size); +/* +* Pages from the page allocator may have data present in +* cache. So flush the cache before using uncached memory. +*/ +dma_cache_sync (NULL , ret, size,DMA_BIDIRECTIONAL ); + *dma_handle =virt_to_phys (ret); } +return ret; +} +EXPORT_SYMBOL (dma_alloc_coherent); + +void dma_free_coherent (struct device *dev,size_t size, +void *vaddr,dma_addr_t dma_handle) +{ +struct dma_coherent_mem *mem = dev ? dev->dma_mem :NULL ; +int order =get_order (size); -return vp; +if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size <<PAGE_SHIFT ))) { +int page = (vaddr - mem->virt_base) >>PAGE_SHIFT ; + +bitmap_release_region (mem->bitmap, page, order); + }else { +WARN_ON (irqs_disabled ());/* for portability */ +BUG_ON (mem && mem->flags &DMA_MEMORY_EXCLUSIVE ); +free_pages ((unsigned long )vaddr, order); + } } -EXPORT_SYMBOL (consistent_alloc); +EXPORT_SYMBOL (dma_free_coherent); -void consistent_free (void *vaddr,size_t size,dma_addr_t dma_handle) +int dma_declare_coherent_memory (struct device *dev,dma_addr_t bus_addr, +dma_addr_t device_addr,size_t size,int flags) { -struct page *page; -unsigned long addr; +void __iomem *mem_base =NULL ; +int pages = size >>PAGE_SHIFT ; +int bitmap_size =BITS_TO_LONGS (pages) *sizeof (long ); + +if ((flags & (DMA_MEMORY_MAP |DMA_MEMORY_IO )) =0 ) + goto out; +if (!size) + goto out; +if (dev->dma_mem) + goto out; + +/* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ + + mem_base =ioremap_nocache (bus_addr, size); +if (!mem_base) + goto out; + + dev->dma_mem =kmalloc (sizeof (struct dma_coherent_mem ),GFP_KERNEL ); +if (!dev->dma_mem) + goto out; + dev->dma_mem->bitmap =kzalloc (bitmap_size,GFP_KERNEL ); +if (!dev->dma_mem->bitmap) + goto free1_out; + + dev->dma_mem->virt_base = mem_base; + dev->dma_mem->device_base = device_addr; + dev->dma_mem->size = pages; + dev->dma_mem->flags = flags; + +if (flags &DMA_MEMORY_MAP ) +return DMA_MEMORY_MAP ; + +return DMA_MEMORY_IO ; + + free1_out: +kfree (dev->dma_mem); + out: +if (mem_base) +iounmap (mem_base); +return 0 ; +} +EXPORT_SYMBOL (dma_declare_coherent_memory); - addr = (unsigned long )phys_to_virt ((unsigned long )dma_handle); - page =virt_to_page (addr); +void dma_release_declared_memory (struct device *dev) +{ +struct dma_coherent_mem *mem = dev->dma_mem; -free_pages (addr,get_order (size)); +if (!mem) +return ; + dev->dma_mem =NULL ; +iounmap (mem->virt_base); +kfree (mem->bitmap); +kfree (mem); +} +EXPORT_SYMBOL (dma_release_declared_memory); -iounmap (vaddr); +void *dma_mark_declared_memory_occupied (struct device *dev, +dma_addr_t device_addr,size_t size) +{ +struct dma_coherent_mem *mem = dev->dma_mem; +int pages = (size + (device_addr & ~PAGE_MASK ) +PAGE_SIZE -1 ) >>PAGE_SHIFT ; +int pos, err; + +if (!mem) +return ERR_PTR (-EINVAL ); + + pos = (device_addr - mem->device_base) >>PAGE_SHIFT ; + err =bitmap_allocate_region (mem->bitmap, pos,get_order (pages)); +if (err !=0 ) +return ERR_PTR (err); +return mem->virt_base + (pos <<PAGE_SHIFT ); } -EXPORT_SYMBOL (consistent_free); +EXPORT_SYMBOL (dma_mark_declared_memory_occupied); -void consistent_sync (void *vaddr,size_t size,int direction) +void dma_cache_sync (struct device *dev,void *vaddr,size_t size, +enum dma_data_direction direction) {#ifdef CONFIG_CPU_SH5 void *p1addr = vaddr;
@@ -94,4 +176,4 @@ void consistent_sync(void *vaddr, size_tBUG (); } } -EXPORT_SYMBOL (consistent_sync); +EXPORT_SYMBOL (dma_cache_sync);
include/asm-sh/dma-mapping.h +20 -36
79 lines not on this page · raw message
Paul Mundt
AppliedRe: [PATCH] sh: declared coherent memory support V2
In reply to Magnus DammQuoted 1 line · Magnus Damm
sh: declared coherent memory support V2
Quoted 1 line · Magnus Damm
sh: use declared coherent memory for dreamcast pci ethernet adapter
Quoted 1 line · Magnus Damm
sh: remove consistent alloc stuff from the machine vector
Applied, thanks.
It would be good if the Dreamcast folk(s) can ensure that there are no regressions with the BBA with the curreng git kernel.
PATCH 1/1 sh: declared coherent memory support V2 fix
In reply to Magnus Dammsh: declared coherent memory support V2 fix
This patch fixes the recently introduced declared coherent memory support. Without this fix a cached memory area is returned by dma_alloc_coherent() - unless dma_declare_coherent_memory() has setup a separate area.
This patch makes sure an uncached memory area is returned. With this patch it is now possible to ping through an rtl8139 interface on r2d-plus.
---
arch/sh/mm/consistent.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-)
arch/sh/mm/consistent.c +19 -11
@@ -26,7 +26,7 @@ struct dma_coherent_mem {void *dma_alloc_coherent (struct device *dev,size_t size,dma_addr_t *dma_handle,gfp_t gfp) { -void *ret; +void *ret, *ret_nocache;struct dma_coherent_mem *mem = dev ? dev->dma_mem :NULL ;int order =get_order (size);
@@ -44,17 +44,24 @@ void *dma_alloc_coherent(struct device * } ret = (void *)__get_free_pages (gfp, order); +if (!ret) +return NULL ; -if (ret !=NULL ) { -memset (ret,0 , size); -/* -* Pages from the page allocator may have data present in -* cache. So flush the cache before using uncached memory. -*/ -dma_cache_sync (NULL , ret, size,DMA_BIDIRECTIONAL ); - *dma_handle =virt_to_phys (ret); +memset (ret,0 , size); +/* +* Pages from the page allocator may have data present in +* cache. So flush the cache before using uncached memory. +*/ +dma_cache_sync (dev, ret, size,DMA_BIDIRECTIONAL ); + + ret_nocache =ioremap_nocache (virt_to_phys (ret), size); +if (!ret_nocache) { +free_pages ((unsigned long )ret, order); +return NULL ; } -return ret; + + *dma_handle =virt_to_phys (ret); +return ret_nocache; }EXPORT_SYMBOL (dma_alloc_coherent);
@@ -71,7 +78,8 @@ void dma_free_coherent(struct device *de }else {WARN_ON (irqs_disabled ());/* for portability */ BUG_ON (mem && mem->flags &DMA_MEMORY_EXCLUSIVE ); -free_pages ((unsigned long )vaddr, order); +free_pages ((unsigned long )phys_to_virt (dma_handle), order); +iounmap (vaddr); } }EXPORT_SYMBOL (dma_free_coherent);
Re: [PATCH] sh: declared coherent memory support V2 fix
In reply to Magnus DammQuoted 10 lines · Magnus Damm
sh: declared coherent memory support V2 fix
This patch fixes the recently introduced declared coherent memory support. Without this fix a cached memory area is returned by
dma_alloc_coherent()- unlessdma_declare_coherent_memory()has setup a separate area.This patch makes sure an uncached memory area is returned. With this patch it is now possible to ping through an rtl8139 interface on r2d-plus.
Signed-off-by Magnus Damm <[email protected]>
Grr, missed this one. For some reason my eyes stopped at 'V2' and didn't see the 'fix' part, so I thought I had already applied this one. I'll roll it in to the next -rc1 batch.