On Fri, 19 Mar 2010, Nicolas Pitre wrote: > A highmem page can have 2 states: virtually mapped in the pkmap area, or > not mapped at all. When it is mapped then page_address() returns a > valid virtual address for it. In that case the cache for that mapping > can be valid, even dirty. So the DMA API will perform cache handling > before/after the DMA operation. > > However, before the page is unmapped, the VIVT cache has to be flushed > for that page. This is why the DMA code currently doesn't bother doing > any L1 cache handling when a highmem page is not mapped -- the cache > just can't refer to such a page. > > But on ARMv6 this is different. The L1 cache is VIPT and it therefore > doesn't have to be flushed as often as a VIVT cache. Still, as far as I > know, the highmem code currently always flush any page to be unmapped. > But somewhere somehow an unmapped highmem page becomes subject to DMA > and apparently can still be L1 cached. But the DMA code doesn't flush > its cache due to the page not being mapped and then problems occur. And here's a patch to test that hypothesis. With this patch, all the issues with highmem on my ARMv6 system are gone. Note this is suboptimal. I still have to open my ARM ARM and see if we can do cache maintenance on the L1 cache using physical addresses to avoid the IRQ disable, PTE setup and TLB flush altogether. Also, with this, a couple cache flushes elsewhere could be removed from the highmem code for VIPT caches. But some of them are tricky and require more thoughts. Oh and yes usage of KM_L2_CACHE in this IRQ disabled context is safe. diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 0da7ecc..79771b0 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -22,8 +22,14 @@ #include <asm/highmem.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <asm/system.h> +#include <asm/kmap_types.h> +#include <asm/fixmap.h> +#include <asm/pgtable.h> #include <asm/sizes.h> +#include "mm.h" + /* Sanity check size */ #if (CONSISTENT_DMA_SIZE % SZ_2M) #error "CONSISTENT_DMA_SIZE must be multiple of 2MiB" @@ -464,6 +470,18 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset, vaddr += offset; op(vaddr, len, dir); kunmap_high(page); + } else if (cache_is_vipt_nonaliasing()) { + unsigned long va, idx, pte, flags; + unsigned long pa = page_to_phys(page); + idx = KM_L2_CACHE + KM_TYPE_NR * smp_processor_id(); + va = __fix_to_virt(FIX_KMAP_BEGIN + idx); + pte = pfn_pte(pa >> PAGE_SHIFT, PAGE_KERNEL); + local_irq_save(flags); + set_pte_ext(TOP_PTE(va), pte, 0); + local_flush_tlb_kernel_page(va); + vaddr = (void*)va + offset; + op(vaddr, len, dir); + local_irq_restore(flags); } } else { vaddr = page_address(page) + offset; -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html