Highmem issues with MMC filesystem

Nicolas Pitre nico at fluxnic.net
Fri Mar 19 16:14:16 EDT 2010


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;



More information about the linux-arm-kernel mailing list