sh: Handle fixmap TLB eviction more coherently.

There was a race in the kmap_coherent() implementation. While we
guarded against preemption, there was nothing preventing eviction of
the pre-faulted fixmap entry from the UTLB. Under certain workloads
this would result in the fixmap entries used for cache colouring being
evicted from the UTLB in the midst of a copy_page().

In addition to pre-faulting, we also make sure to preserve the PTEs
in the kernel page table and introduce a cached PTE for kmap_coherent()
usage. This follows a similar change on MIPS ("[MIPS] Fix aliasing bug
in copy_to_user_page / copy_from_user_page").

Reported-by: Hideo Saito <saito@densan.co.jp>
Reported-by: CHIKAMA Masaki <masaki.chikama@gmail.com>
Tested-by: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>

+32 -3
+6
arch/sh/include/asm/pgtable.h
··· 148 148 extern void page_table_range_init(unsigned long start, unsigned long end, 149 149 pgd_t *pgd); 150 150 151 + #if !defined(CONFIG_CACHE_OFF) && defined(CONFIG_CPU_SH4) && defined(CONFIG_MMU) 152 + extern void kmap_coherent_init(void); 153 + #else 154 + #define kmap_coherent_init() do { } while (0) 155 + #endif 156 + 151 157 #include <asm-generic/pgtable.h> 152 158 153 159 #endif /* __ASM_SH_PGTABLE_H */
+9 -3
arch/sh/mm/init.c
··· 137 137 void __init paging_init(void) 138 138 { 139 139 unsigned long max_zone_pfns[MAX_NR_ZONES]; 140 + unsigned long vaddr; 140 141 int nid; 141 142 142 143 /* We don't need to map the kernel through the TLB, as ··· 149 148 * check for a null value. */ 150 149 set_TTB(swapper_pg_dir); 151 150 152 - /* Populate the relevant portions of swapper_pg_dir so that 151 + /* 152 + * Populate the relevant portions of swapper_pg_dir so that 153 153 * we can use the fixmap entries without calling kmalloc. 154 - * pte's will be filled in by __set_fixmap(). */ 155 - page_table_range_init(FIXADDR_START, FIXADDR_TOP, swapper_pg_dir); 154 + * pte's will be filled in by __set_fixmap(). 155 + */ 156 + vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; 157 + page_table_range_init(vaddr, 0, swapper_pg_dir); 158 + 159 + kmap_coherent_init(); 156 160 157 161 memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); 158 162
+17
arch/sh/mm/pg-sh4.c
··· 7 7 * Released under the terms of the GNU GPL v2.0. 8 8 */ 9 9 #include <linux/mm.h> 10 + #include <linux/init.h> 10 11 #include <linux/mutex.h> 11 12 #include <linux/fs.h> 12 13 #include <linux/highmem.h> ··· 16 15 #include <asm/cacheflush.h> 17 16 18 17 #define CACHE_ALIAS (current_cpu_data.dcache.alias_mask) 18 + 19 + #define kmap_get_fixmap_pte(vaddr) \ 20 + pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)), (vaddr)) 21 + 22 + static pte_t *kmap_coherent_pte; 23 + 24 + void __init kmap_coherent_init(void) 25 + { 26 + unsigned long vaddr; 27 + 28 + /* cache the first coherent kmap pte */ 29 + vaddr = __fix_to_virt(FIX_CMAP_BEGIN); 30 + kmap_coherent_pte = kmap_get_fixmap_pte(vaddr); 31 + } 19 32 20 33 static inline void *kmap_coherent(struct page *page, unsigned long addr) 21 34 { ··· 48 33 local_irq_restore(flags); 49 34 50 35 update_mmu_cache(NULL, vaddr, pte); 36 + 37 + set_pte(kmap_coherent_pte - (FIX_CMAP_END - idx), pte); 51 38 52 39 return (void *)vaddr; 53 40 }