Skip to content

Commit

Permalink
ARM: dma-mapping: factor dma_free_coherent() common code
Browse files Browse the repository at this point in the history
We effectively have three implementations of dma_free_coherent() mixed up
in the code; the incoherent MMU, coherent MMU and noMMU versions.

The coherent MMU and noMMU versions are actually functionally identical.
The incoherent MMU version is almost the same, but with the additional
step of unmapping the secondary mapping.

Separate out this additional step into __dma_free_remap() and simplify
the resulting dma_free_coherent() code.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Acked-by: Greg Ungerer <gerg@uclinux.org>
  • Loading branch information
Russell King committed Nov 24, 2009
1 parent 04da569 commit 695ae0a
Showing 1 changed file with 68 additions and 73 deletions.
141 changes: 68 additions & 73 deletions arch/arm/mm/dma-mapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,70 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
*handle = ~0;
return NULL;
}

static void __dma_free_remap(void *cpu_addr, size_t size)
{
struct arm_vmregion *c;
unsigned long addr;
pte_t *ptep;
int idx;
u32 off;

c = arm_vmregion_find_remove(&consistent_head, (unsigned long)cpu_addr);
if (!c) {
printk(KERN_ERR "%s: trying to free invalid coherent area: %p\n",
__func__, cpu_addr);
dump_stack();
return;
}

if ((c->vm_end - c->vm_start) != size) {
printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n",
__func__, c->vm_end - c->vm_start, size);
dump_stack();
size = c->vm_end - c->vm_start;
}

idx = CONSISTENT_PTE_INDEX(c->vm_start);
off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1);
ptep = consistent_pte[idx] + off;
addr = c->vm_start;
do {
pte_t pte = ptep_get_and_clear(&init_mm, addr, ptep);
unsigned long pfn;

ptep++;
addr += PAGE_SIZE;
off++;
if (off >= PTRS_PER_PTE) {
off = 0;
ptep = consistent_pte[++idx];
}

if (!pte_none(pte) && pte_present(pte)) {
pfn = pte_pfn(pte);

if (pfn_valid(pfn)) {
struct page *page = pfn_to_page(pfn);

/*
* x86 does not mark the pages reserved...
*/
ClearPageReserved(page);
continue;
}
}
printk(KERN_CRIT "%s: bad page in kernel page table\n",
__func__);
} while (size -= PAGE_SIZE);

flush_tlb_kernel_range(c->vm_start, c->vm_end);

arm_vmregion_free(&consistent_head, c);
}

#else /* !CONFIG_MMU */

static void *
__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
pgprot_t prot)
Expand All @@ -224,6 +287,9 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
*handle = page_to_dma(dev, page);
return page_address(page);
}

#define __dma_free_remap(addr, size) do { } while (0)

#endif /* CONFIG_MMU */

/*
Expand Down Expand Up @@ -317,91 +383,20 @@ EXPORT_SYMBOL(dma_mmap_writecombine);
* free a page as defined by the above mapping.
* Must not be called with IRQs disabled.
*/
#ifdef CONFIG_MMU
void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle)
{
struct arm_vmregion *c;
unsigned long addr;
pte_t *ptep;
int idx;
u32 off;

WARN_ON(irqs_disabled());

if (dma_release_from_coherent(dev, get_order(size), cpu_addr))
return;

size = PAGE_ALIGN(size);

if (arch_is_coherent()) {
__dma_free_buffer(dma_to_page(dev, handle), size);
return;
}

c = arm_vmregion_find_remove(&consistent_head, (unsigned long)cpu_addr);
if (!c)
goto no_area;

if ((c->vm_end - c->vm_start) != size) {
printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n",
__func__, c->vm_end - c->vm_start, size);
dump_stack();
size = c->vm_end - c->vm_start;
}

idx = CONSISTENT_PTE_INDEX(c->vm_start);
off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1);
ptep = consistent_pte[idx] + off;
addr = c->vm_start;
do {
pte_t pte = ptep_get_and_clear(&init_mm, addr, ptep);
unsigned long pfn;

ptep++;
addr += PAGE_SIZE;
off++;
if (off >= PTRS_PER_PTE) {
off = 0;
ptep = consistent_pte[++idx];
}

if (!pte_none(pte) && pte_present(pte)) {
pfn = pte_pfn(pte);

if (pfn_valid(pfn)) {
struct page *page = pfn_to_page(pfn);

/*
* x86 does not mark the pages reserved...
*/
ClearPageReserved(page);
continue;
}
}
printk(KERN_CRIT "%s: bad page in kernel page table\n",
__func__);
} while (size -= PAGE_SIZE);

flush_tlb_kernel_range(c->vm_start, c->vm_end);

arm_vmregion_free(&consistent_head, c);
if (!arch_is_coherent())
__dma_free_remap(cpu_addr, size);

__dma_free_buffer(dma_to_page(dev, handle), size);
return;

no_area:
printk(KERN_ERR "%s: trying to free invalid coherent area: %p\n",
__func__, cpu_addr);
dump_stack();
}
#else /* !CONFIG_MMU */
void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle)
{
if (dma_release_from_coherent(dev, get_order(size), cpu_addr))
return;
__dma_free_buffer(dma_to_page(dev, handle), PAGE_ALIGN(size));
}
#endif /* CONFIG_MMU */
EXPORT_SYMBOL(dma_free_coherent);

/*
Expand Down

0 comments on commit 695ae0a

Please sign in to comment.