diff options
author | Vandana Salve <vsalve@nvidia.com> | 2013-11-21 16:41:15 +0530 |
---|---|---|
committer | Hiroshi Doyu <hdoyu@nvidia.com> | 2013-11-28 23:01:33 -0800 |
commit | f5427b266fe43a53997dcaf7ec19b5e87a84033a (patch) | |
tree | e53ef470660397b76436b17b2137a40f12cbec9a /drivers/base | |
parent | e4cde18e622451d86bd9fd8df24ede94f347bde0 (diff) |
base: dma-coherent: add dma exact size alloc/release functions
Add functions dma_alloc_from_coherent_attr and
dma_release_from_coherent_attr. use attribute
DMA_ATTR_ALLOC_EXACT_SIZE to allocate/release
memory of exact size rather than order of 2^pages to
reduce internal fragmentation
bug 1380639
Change-Id: Ia7f560bf94960dfa9570f5be597f7f25b49150e1
Signed-off-by: Vandana Salve <vsalve@nvidia.com>
Reviewed-on: http://git-master/r/333986
Reviewed-by: Hiroshi Doyu <hdoyu@nvidia.com>
Tested-by: Hiroshi Doyu <hdoyu@nvidia.com>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/dma-coherent.c | 60 |
1 files changed, 45 insertions, 15 deletions
diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index 8ca472af251b..10c73a1f8bcf 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -6,6 +6,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/dma-mapping.h> +#include <linux/dma-attrs.h> struct dma_coherent_mem { void *virt_base; @@ -107,26 +108,30 @@ void *dma_mark_declared_memory_occupied(struct device *dev, EXPORT_SYMBOL(dma_mark_declared_memory_occupied); /** - * dma_alloc_from_coherent() - try to allocate memory from the per-device coherent area + * dma_alloc_from_coherent_attr() - try to allocate memory from the per-device + * coherent area * * @dev: device from which we allocate memory * @size: size of requested memory area * @dma_handle: This will be filled with the correct dma handle * @ret: This pointer will be filled with the virtual address * to allocated area. - * + * @attrs: DMA Attribute * This function should be only called from per-arch dma_alloc_coherent() * to support allocation from per-device coherent memory pools. * - * Returns 0 if dma_alloc_coherent should continue with allocating from + * Returns 0 if dma_alloc_coherent_attr should continue with allocating from * generic memory areas, or !0 if dma_alloc_coherent should return @ret. */ -int dma_alloc_from_coherent(struct device *dev, ssize_t size, - dma_addr_t *dma_handle, void **ret) +int dma_alloc_from_coherent_attr(struct device *dev, ssize_t size, + dma_addr_t *dma_handle, void **ret, + struct dma_attrs *attrs) { struct dma_coherent_mem *mem; int order = get_order(size); int pageno; + unsigned int count; + unsigned long align; if (!dev) return 0; @@ -139,10 +144,22 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size, if (unlikely(size > (mem->size << PAGE_SHIFT))) goto err; - pageno = bitmap_find_free_region(mem->bitmap, mem->size, order); - if (unlikely(pageno < 0)) + if (dma_get_attr(DMA_ATTR_ALLOC_EXACT_SIZE, attrs)) { + align = 0; + count = PAGE_ALIGN(size) >> PAGE_SHIFT; + } else { + align = (1 << order) - 1; + count = 1 << order; + } + + pageno = bitmap_find_next_zero_area(mem->bitmap, mem->size, + 0, count, align); + + if (pageno >= mem->size) goto err; + bitmap_set(mem->bitmap, pageno, count); + /* * Memory was found in the per-device area. */ @@ -162,25 +179,30 @@ err: */ return mem->flags & DMA_MEMORY_EXCLUSIVE; } -EXPORT_SYMBOL(dma_alloc_from_coherent); +EXPORT_SYMBOL(dma_alloc_from_coherent_attr); /** - * dma_release_from_coherent() - try to free the memory allocated from per-device coherent memory pool + * dma_release_from_coherent_attr() - try to free the memory allocated from + * per-device coherent memory pool * @dev: device from which the memory was allocated - * @order: the order of pages allocated + * @size: size of the memory area to free * @vaddr: virtual address of allocated pages + * @attrs: DMA Attribute * * This checks whether the memory was allocated from the per-device * coherent memory pool and if so, releases that memory. * * Returns 1 if we correctly released the memory, or 0 if - * dma_release_coherent() should proceed with releasing memory from + * dma_release_coherent_attr() should proceed with releasing memory from * generic pools. */ -int dma_release_from_coherent(struct device *dev, int order, void *vaddr) +int dma_release_from_coherent_attr(struct device *dev, size_t size, void *vaddr, + struct dma_attrs *attrs) { struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; void *mem_addr; + unsigned int count; + unsigned int pageno; if (!mem) return 0; @@ -190,16 +212,24 @@ int dma_release_from_coherent(struct device *dev, int order, void *vaddr) else mem_addr = mem->virt_base; + if (mem && vaddr >= mem_addr && vaddr - mem_addr < mem->size << PAGE_SHIFT) { - int page = (vaddr - mem_addr) >> PAGE_SHIFT; - bitmap_release_region(mem->bitmap, page, order); + pageno = (vaddr - mem_addr) >> PAGE_SHIFT; + + if (dma_get_attr(DMA_ATTR_ALLOC_EXACT_SIZE, attrs)) + count = PAGE_ALIGN(size) >> PAGE_SHIFT; + else + count = 1 << get_order(size); + + bitmap_clear(mem->bitmap, pageno, count); + return 1; } return 0; } -EXPORT_SYMBOL(dma_release_from_coherent); +EXPORT_SYMBOL(dma_release_from_coherent_attr); /** * dma_mmap_from_coherent() - try to mmap the memory allocated from |