On 12/03/2014 04:07 PM, Lars-Peter Clausen wrote: > When running in non-cache coherent configuration the memory that was > allocated with dma_alloc_coherent() has a custom mapping and so there is no > 1-to-1 relationship between the kernel virtual address and the PFN. This > means that virt_to_pfn() will not work correctly for those addresses and the > default mmap implementation in the form of dma_common_mmap() will map some > random, but not the requested, memory area. > > Fix this by providing a custom mmap implementation that looks up the PFN > from the page table rather than using virt_to_pfn. > > Signed-off-by: Lars-Peter Clausen <l...@metafoo.de> > --- > arch/microblaze/include/asm/pgtable.h | 1 + > arch/microblaze/kernel/dma.c | 26 ++++++++++++++++++++++++++ > arch/microblaze/mm/consistent.c | 23 ++++++++++++++++++----- > 3 files changed, 45 insertions(+), 5 deletions(-) > > diff --git a/arch/microblaze/include/asm/pgtable.h > b/arch/microblaze/include/asm/pgtable.h > index 95cef0b..df19d0c 100644 > --- a/arch/microblaze/include/asm/pgtable.h > +++ b/arch/microblaze/include/asm/pgtable.h > @@ -565,6 +565,7 @@ void consistent_free(size_t size, void *vaddr); > void consistent_sync(void *vaddr, size_t size, int direction); > void consistent_sync_page(struct page *page, unsigned long offset, > size_t size, int direction); > +unsigned long consistent_virt_to_pfn(void *vaddr); > > void setup_memory(void); > #endif /* __ASSEMBLY__ */ > diff --git a/arch/microblaze/kernel/dma.c b/arch/microblaze/kernel/dma.c > index 4633c36..4822367 100644 > --- a/arch/microblaze/kernel/dma.c > +++ b/arch/microblaze/kernel/dma.c > @@ -154,9 +154,35 @@ dma_direct_sync_sg_for_device(struct device *dev, > __dma_sync(sg->dma_address, sg->length, direction); > } > > +int dma_direct_mmap_coherent(struct device *dev, struct vm_area_struct *vma, > + void *cpu_addr, dma_addr_t handle, size_t size, > + struct dma_attrs *attrs) > +{ > + unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; > + unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT; > + unsigned long off = vma->vm_pgoff; > + unsigned long pfn; > + > + if (!IS_ENABLED(CONFIG_MMU)) > + return -ENXIO; > + > + if (off >= count || user_count > (count - off)) > + return -ENXIO; > + > +#ifdef NOT_COHERENT_CACHE > + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); > + pfn = consistent_virt_to_pfn(cpu_addr); > +#else > + pfn = virt_to_pfn(cpu_addr); > +#endif > + return remap_pfn_range(vma, vma->vm_start, pfn + off, > + vma->vm_end - vma->vm_start, vma->vm_page_prot); > +} > + > struct dma_map_ops dma_direct_ops = { > .alloc = dma_direct_alloc_coherent, > .free = dma_direct_free_coherent, > + .mmap = dma_direct_mmap_coherent, > .map_sg = dma_direct_map_sg, > .dma_supported = dma_direct_dma_supported, > .map_page = dma_direct_map_page, > diff --git a/arch/microblaze/mm/consistent.c b/arch/microblaze/mm/consistent.c > index e10ad93..f358cd2 100644 > --- a/arch/microblaze/mm/consistent.c > +++ b/arch/microblaze/mm/consistent.c > @@ -156,6 +156,23 @@ void *consistent_alloc(gfp_t gfp, size_t size, > dma_addr_t *dma_handle) > } > EXPORT_SYMBOL(consistent_alloc); > > +static pte_t *consistent_virt_to_pte(void *vaddr) > +{ > + unsigned long addr = (unsigned long)vaddr; > + > + return pte_offset_kernel(pmd_offset(pgd_offset_k(addr), addr), addr); > +} > + > +unsigned long consistent_virt_to_pfn(void *vaddr) > +{ > + pte_t *ptep = consistent_virt_to_pte(vaddr); > + > + if (pte_none(*ptep) || !pte_present(*ptep)) > + return 0; > + > + return pte_pfn(*ptep); > +} > + > /* > * free page(s) as defined by the above mapping. > */ > @@ -181,13 +198,9 @@ void consistent_free(size_t size, void *vaddr) > } while (size -= PAGE_SIZE); > #else > do { > - pte_t *ptep; > + pte_t *ptep = consistent_virt_to_pte(vaddr); > unsigned long pfn; > > - ptep = pte_offset_kernel(pmd_offset(pgd_offset_k( > - (unsigned int)vaddr), > - (unsigned int)vaddr), > - (unsigned int)vaddr); > if (!pte_none(*ptep) && pte_present(*ptep)) { > pfn = pte_pfn(*ptep); > pte_clear(&init_mm, (unsigned int)vaddr, ptep); >
Applied. Thanks, Michal -- Michal Simek, Ing. (M.Eng), OpenPGP -> KeyID: FE3D1F91 w: www.monstr.eu p: +42-0-721842854 Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/ Maintainer of Linux kernel - Xilinx Zynq ARM architecture Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform
signature.asc
Description: OpenPGP digital signature