forked from Minki/linux
7e5a69e83b
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@nvidia.com> that those pages ended up being unmapped during cache maintenance on SMP causing segmentation faults. Signed-off-by: Nicolas Pitre <nico@marvell.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
45 lines
1.2 KiB
C
45 lines
1.2 KiB
C
#ifndef _ASM_HIGHMEM_H
|
|
#define _ASM_HIGHMEM_H
|
|
|
|
#include <asm/kmap_types.h>
|
|
|
|
#define PKMAP_BASE (PAGE_OFFSET - PMD_SIZE)
|
|
#define LAST_PKMAP PTRS_PER_PTE
|
|
#define LAST_PKMAP_MASK (LAST_PKMAP - 1)
|
|
#define PKMAP_NR(virt) (((virt) - PKMAP_BASE) >> PAGE_SHIFT)
|
|
#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT))
|
|
|
|
#define kmap_prot PAGE_KERNEL
|
|
|
|
#define flush_cache_kmaps() \
|
|
do { \
|
|
if (cache_is_vivt()) \
|
|
flush_cache_all(); \
|
|
} while (0)
|
|
|
|
extern pte_t *pkmap_page_table;
|
|
|
|
#define ARCH_NEEDS_KMAP_HIGH_GET
|
|
|
|
extern void *kmap_high(struct page *page);
|
|
extern void *kmap_high_get(struct page *page);
|
|
extern void kunmap_high(struct page *page);
|
|
|
|
extern void *kmap_high_l1_vipt(struct page *page, pte_t *saved_pte);
|
|
extern void kunmap_high_l1_vipt(struct page *page, pte_t saved_pte);
|
|
|
|
/*
|
|
* The following functions are already defined by <linux/highmem.h>
|
|
* when CONFIG_HIGHMEM is not set.
|
|
*/
|
|
#ifdef CONFIG_HIGHMEM
|
|
extern void *kmap(struct page *page);
|
|
extern void kunmap(struct page *page);
|
|
extern void *kmap_atomic(struct page *page, enum km_type type);
|
|
extern void kunmap_atomic(void *kvaddr, enum km_type type);
|
|
extern void *kmap_atomic_pfn(unsigned long pfn, enum km_type type);
|
|
extern struct page *kmap_atomic_to_page(const void *ptr);
|
|
#endif
|
|
|
|
#endif
|