Highmem issues with MMC filesystem
there's more changes between ARMv5 and ARMv6 than just
the cache model. There's weak memory ordering effects too.
at the moment we have:
- ARMv5 without highmem -> works
- ARMv5 with highmem -> works
- ARMv6 without highmem -> works
- ARMv6 with highmem -> fails
In all four cases the SATA driver, the hard disk and the filesystem on
it are identical. In the two ARMv5 cases the system and the kernel are
identical. Ditto for the two ARMv6 cases.
And the kernel used is the very latest i.e. v2.6.34-rc1-1642-gfc7f99c.
Marvell 88AP510 aka Dove. But the same issue was reported on OMAP.
Any block device using DMA is affected. Drivers using PIO are not,
neither is NFS. And again, using the _same_ kernel with mem=512m so to
be sure highmem doesn't kick in exhibits no issue even with DMA.
[PATCH] ARM: fix highmem with VIPT cache and DMA
The VIVT cache of a highmem page is always flushed before the page is unmapped. This cache flush is explicit through flush_cache_kmaps() in flush_all_zero_pkmaps(), or through _cpuc_flush_dcache_area() in kunmap_atomic(). There is also an implicit flush of those highmem pages that were part of a process that just terminated making those pages free as the whole VIVT cache has to be flushed on every task switch. Hence unmapped highmem pages need no cache maintenance in that case.
However unmapped pages may still be cached with a VIPT cache because the cache is tagged with physical addresses. There is no need for a whole cache flush during task switching for that reason, and despite the explicit cache flushes in flush_all_zero_pkmaps() and kunmap_atomic(), some highmem pages that were mapped in user space end up still cached even when they become unmapped.
So, we do have to perform cache maintenance on those unmapped highmem pages in the context of DMA when using a VIPT cache. Unfortunately, it is not possible to perform that cache maintenance using physical addresses as all the L1 cache maintenance coprocessor functions accept virtual addresses only. Therefore we have no choice but to set up a temporary virtual mapping for that purpose.
And of course the explicit cache flushing when unmapping a highmem page on a system with a VIPT cache now can go, which should increase performance.
While at it, because the code in __flush_dcache_page() has to be modified anyway, let's also make sure the mapped highmem pages are pinned with kmap_high_get() for the duration of the cache maintenance operation. Because kunmap() does unmap highmem pages lazily, it was reported by Gary King <GKing@xxxxxxxxxx> that those pages ended up being unmapped during cache maintenance on SMP causing segmentation faults.