Commit 5bc23d32 authored by Russell King's avatar Russell King

ARM: DMA coherent allocator: align remapped addresses

The DMA coherent remap area is used to provide an uncached mapping
of memory for coherency with DMA engines.  Currently, we look for
any free hole which our allocation will fit in with page alignment.

However, this can lead to fragmentation of the area, and allows small
allocations to cross L1 entry boundaries.  This is undesirable as we
want to move towards allocating sections of memory.

Align allocations according to the size, limiting the alignment between
the page and section sizes.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent d7461963
...@@ -183,6 +183,8 @@ static void * ...@@ -183,6 +183,8 @@ static void *
__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot) __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot)
{ {
struct arm_vmregion *c; struct arm_vmregion *c;
size_t align;
int bit;
if (!consistent_pte[0]) { if (!consistent_pte[0]) {
printk(KERN_ERR "%s: not initialised\n", __func__); printk(KERN_ERR "%s: not initialised\n", __func__);
...@@ -190,10 +192,21 @@ __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot) ...@@ -190,10 +192,21 @@ __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot)
return NULL; return NULL;
} }
/*
* Align the virtual region allocation - maximum alignment is
* a section size, minimum is a page size. This helps reduce
* fragmentation of the DMA space, and also prevents allocations
* smaller than a section from crossing a section boundary.
*/
bit = fls(size - 1) + 1;
if (bit > SECTION_SHIFT)
bit = SECTION_SHIFT;
align = 1 << bit;
/* /*
* Allocate a virtual address in the consistent mapping region. * Allocate a virtual address in the consistent mapping region.
*/ */
c = arm_vmregion_alloc(&consistent_head, size, c = arm_vmregion_alloc(&consistent_head, align, size,
gfp & ~(__GFP_DMA | __GFP_HIGHMEM)); gfp & ~(__GFP_DMA | __GFP_HIGHMEM));
if (c) { if (c) {
pte_t *pte; pte_t *pte;
......
...@@ -35,7 +35,8 @@ ...@@ -35,7 +35,8 @@
*/ */
struct arm_vmregion * struct arm_vmregion *
arm_vmregion_alloc(struct arm_vmregion_head *head, size_t size, gfp_t gfp) arm_vmregion_alloc(struct arm_vmregion_head *head, size_t align,
size_t size, gfp_t gfp)
{ {
unsigned long addr = head->vm_start, end = head->vm_end - size; unsigned long addr = head->vm_start, end = head->vm_end - size;
unsigned long flags; unsigned long flags;
...@@ -58,7 +59,7 @@ arm_vmregion_alloc(struct arm_vmregion_head *head, size_t size, gfp_t gfp) ...@@ -58,7 +59,7 @@ arm_vmregion_alloc(struct arm_vmregion_head *head, size_t size, gfp_t gfp)
goto nospc; goto nospc;
if ((addr + size) <= c->vm_start) if ((addr + size) <= c->vm_start)
goto found; goto found;
addr = c->vm_end; addr = ALIGN(c->vm_end, align);
if (addr > end) if (addr > end)
goto nospc; goto nospc;
} }
......
...@@ -21,7 +21,7 @@ struct arm_vmregion { ...@@ -21,7 +21,7 @@ struct arm_vmregion {
int vm_active; int vm_active;
}; };
struct arm_vmregion *arm_vmregion_alloc(struct arm_vmregion_head *, size_t, gfp_t); struct arm_vmregion *arm_vmregion_alloc(struct arm_vmregion_head *, size_t, size_t, gfp_t);
struct arm_vmregion *arm_vmregion_find(struct arm_vmregion_head *, unsigned long); struct arm_vmregion *arm_vmregion_find(struct arm_vmregion_head *, unsigned long);
struct arm_vmregion *arm_vmregion_find_remove(struct arm_vmregion_head *, unsigned long); struct arm_vmregion *arm_vmregion_find_remove(struct arm_vmregion_head *, unsigned long);
void arm_vmregion_free(struct arm_vmregion_head *, struct arm_vmregion *); void arm_vmregion_free(struct arm_vmregion_head *, struct arm_vmregion *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment