Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Russell King <rmk+kernel@arm.linux.org.uk>

+18 -4
+14 -1
arch/arm/mm/dma-mapping.c
··· 183 183 __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot) 184 184 { 185 185 struct arm_vmregion *c; 186 + size_t align; 187 + int bit; 186 188 187 189 if (!consistent_pte[0]) { 188 190 printk(KERN_ERR "%s: not initialised\n", __func__); ··· 193 191 } 194 192 195 193 /* 194 + * Align the virtual region allocation - maximum alignment is 195 + * a section size, minimum is a page size. This helps reduce 196 + * fragmentation of the DMA space, and also prevents allocations 197 + * smaller than a section from crossing a section boundary. 198 + */ 199 + bit = fls(size - 1) + 1; 200 + if (bit > SECTION_SHIFT) 201 + bit = SECTION_SHIFT; 202 + align = 1 << bit; 203 + 204 + /* 196 205 * Allocate a virtual address in the consistent mapping region. 197 206 */ 198 - c = arm_vmregion_alloc(&consistent_head, size, 207 + c = arm_vmregion_alloc(&consistent_head, align, size, 199 208 gfp & ~(__GFP_DMA | __GFP_HIGHMEM)); 200 209 if (c) { 201 210 pte_t *pte;
+3 -2
arch/arm/mm/vmregion.c
··· 35 35 */ 36 36 37 37 struct arm_vmregion * 38 - arm_vmregion_alloc(struct arm_vmregion_head *head, size_t size, gfp_t gfp) 38 + arm_vmregion_alloc(struct arm_vmregion_head *head, size_t align, 39 + size_t size, gfp_t gfp) 39 40 { 40 41 unsigned long addr = head->vm_start, end = head->vm_end - size; 41 42 unsigned long flags; ··· 59 58 goto nospc; 60 59 if ((addr + size) <= c->vm_start) 61 60 goto found; 62 - addr = c->vm_end; 61 + addr = ALIGN(c->vm_end, align); 63 62 if (addr > end) 64 63 goto nospc; 65 64 }
+1 -1
arch/arm/mm/vmregion.h
··· 21 21 int vm_active; 22 22 }; 23 23 24 - struct arm_vmregion *arm_vmregion_alloc(struct arm_vmregion_head *, size_t, gfp_t); 24 + struct arm_vmregion *arm_vmregion_alloc(struct arm_vmregion_head *, size_t, size_t, gfp_t); 25 25 struct arm_vmregion *arm_vmregion_find(struct arm_vmregion_head *, unsigned long); 26 26 struct arm_vmregion *arm_vmregion_find_remove(struct arm_vmregion_head *, unsigned long); 27 27 void arm_vmregion_free(struct arm_vmregion_head *, struct arm_vmregion *);