Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xtensa/mm/highmem: Make generic kmap_atomic() work correctly

The conversion to the generic kmap_atomic() implementation missed the fact
that xtensa's fixmap works bottom up while all other implementations work
top down. There is no real reason why xtensa needs to work that way.

Cure it by:

- Using the generic fix_to_virt()/virt_to_fix() functions which work top
down
- Adjusting the mapping defines
- Using the generic index calculation for the non cache aliasing case
- Making the cache colour offset reverse so the effective index is correct

While at it, remove the outdated and misleading comment above the fixmap
enum which originates from the initial copy&pasta of this code from i386.

[ Max: Fixed the off by one in the index calculation ]

Fixes: 629ed3f7dad2 ("xtensa/mm/highmem: Switch to generic kmap atomic")
Reported-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Max Filippov <jcmvbkbc@gmail.com>
Link: https://lore.kernel.org/r/20201116193253.23875-1-jcmvbkbc@gmail.com

+31 -64
+7 -48
arch/xtensa/include/asm/fixmap.h
··· 17 17 #include <linux/threads.h> 18 18 #include <linux/pgtable.h> 19 19 #include <asm/kmap_size.h> 20 - #endif 21 20 22 - /* 23 - * Here we define all the compile-time 'special' virtual 24 - * addresses. The point is to have a constant address at 25 - * compile time, but to set the physical address only 26 - * in the boot process. We allocate these special addresses 27 - * from the start of the consistent memory region upwards. 28 - * Also this lets us do fail-safe vmalloc(), we 29 - * can guarantee that these special addresses and 30 - * vmalloc()-ed addresses never overlap. 31 - * 32 - * these 'compile-time allocated' memory buffers are 33 - * fixed-size 4k pages. (or larger if used with an increment 34 - * higher than 1) use fixmap_set(idx,phys) to associate 35 - * physical memory with fixmap indices. 36 - */ 21 + /* The map slots for temporary mappings via kmap_atomic/local(). */ 37 22 enum fixed_addresses { 38 - #ifdef CONFIG_HIGHMEM 39 - /* reserved pte's for temporary kernel mappings */ 40 23 FIX_KMAP_BEGIN, 41 24 FIX_KMAP_END = FIX_KMAP_BEGIN + 42 25 (KM_MAX_IDX * NR_CPUS * DCACHE_N_COLORS) - 1, 43 - #endif 44 26 __end_of_fixed_addresses 45 27 }; 46 28 47 - #define FIXADDR_TOP (XCHAL_KSEG_CACHED_VADDR - PAGE_SIZE) 29 + #define FIXADDR_END (XCHAL_KSEG_CACHED_VADDR - PAGE_SIZE) 48 30 #define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) 49 - #define FIXADDR_START ((FIXADDR_TOP - FIXADDR_SIZE) & PMD_MASK) 31 + /* Enforce that FIXADDR_START is PMD aligned to handle cache aliasing */ 32 + #define FIXADDR_START ((FIXADDR_END - FIXADDR_SIZE) & PMD_MASK) 33 + #define FIXADDR_TOP (FIXADDR_START + FIXADDR_SIZE - PAGE_SIZE) 50 34 51 - #define __fix_to_virt(x) (FIXADDR_START + ((x) << PAGE_SHIFT)) 52 - #define __virt_to_fix(x) (((x) - FIXADDR_START) >> PAGE_SHIFT) 35 + #include <asm-generic/fixmap.h> 53 36 54 - #ifndef __ASSEMBLY__ 55 - /* 56 - * 'index to address' translation. If anyone tries to use the idx 57 - * directly without translation, we catch the bug with a NULL-deference 58 - * kernel oops. Illegal ranges of incoming indices are caught too. 59 - */ 60 - static __always_inline unsigned long fix_to_virt(const unsigned int idx) 61 - { 62 - /* Check if this memory layout is broken because fixmap overlaps page 63 - * table. 64 - */ 65 - BUILD_BUG_ON(FIXADDR_START < 66 - TLBTEMP_BASE_1 + TLBTEMP_SIZE); 67 - BUILD_BUG_ON(idx >= __end_of_fixed_addresses); 68 - return __fix_to_virt(idx); 69 - } 70 - 71 - static inline unsigned long virt_to_fix(const unsigned long vaddr) 72 - { 73 - BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START); 74 - return __virt_to_fix(vaddr); 75 - } 76 - 77 - #endif 78 - 37 + #endif /* CONFIG_HIGHMEM */ 79 38 #endif
+9 -6
arch/xtensa/include/asm/highmem.h
··· 12 12 #ifndef _XTENSA_HIGHMEM_H 13 13 #define _XTENSA_HIGHMEM_H 14 14 15 + #ifdef CONFIG_HIGHMEM 15 16 #include <linux/wait.h> 16 17 #include <linux/pgtable.h> 17 18 #include <asm/cacheflush.h> ··· 59 58 { 60 59 return pkmap_map_wait_arr + color; 61 60 } 61 + 62 + enum fixed_addresses kmap_local_map_idx(int type, unsigned long pfn); 63 + #define arch_kmap_local_map_idx kmap_local_map_idx 64 + 65 + enum fixed_addresses kmap_local_unmap_idx(int type, unsigned long addr); 66 + #define arch_kmap_local_unmap_idx kmap_local_unmap_idx 67 + 62 68 #endif 63 69 64 70 extern pte_t *pkmap_page_table; ··· 75 67 flush_cache_all(); 76 68 } 77 69 78 - enum fixed_addresses kmap_local_map_idx(int type, unsigned long pfn); 79 - #define arch_kmap_local_map_idx kmap_local_map_idx 80 - 81 - enum fixed_addresses kmap_local_unmap_idx(int type, unsigned long addr); 82 - #define arch_kmap_local_unmap_idx kmap_local_unmap_idx 83 - 84 70 #define arch_kmap_local_post_unmap(vaddr) \ 85 71 local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE) 86 72 87 73 void kmap_init(void); 88 74 75 + #endif /* CONFIG_HIGHMEM */ 89 76 #endif
+11 -7
arch/xtensa/mm/highmem.c
··· 23 23 for (i = 0; i < ARRAY_SIZE(pkmap_map_wait_arr); ++i) 24 24 init_waitqueue_head(pkmap_map_wait_arr + i); 25 25 } 26 - #else 27 - static inline void kmap_waitqueues_init(void) 28 - { 29 - } 30 - #endif 31 26 32 27 static inline enum fixed_addresses kmap_idx(int type, unsigned long color) 33 28 { 34 - return (type + KM_MAX_IDX * smp_processor_id()) * DCACHE_N_COLORS + 35 - color; 29 + int idx = (type + KM_MAX_IDX * smp_processor_id()) * DCACHE_N_COLORS; 30 + 31 + /* 32 + * The fixmap operates top down, so the color offset needs to be 33 + * reverse as well. 34 + */ 35 + return idx + DCACHE_N_COLORS - 1 - color; 36 36 } 37 37 38 38 enum fixed_addresses kmap_local_map_idx(int type, unsigned long pfn) ··· 44 44 { 45 45 return kmap_idx(type, DCACHE_ALIAS(addr)); 46 46 } 47 + 48 + #else 49 + static inline void kmap_waitqueues_init(void) { } 50 + #endif 47 51 48 52 void __init kmap_init(void) 49 53 {
+2 -2
arch/xtensa/mm/init.c
··· 147 147 #ifdef CONFIG_HIGHMEM 148 148 PKMAP_BASE, PKMAP_BASE + LAST_PKMAP * PAGE_SIZE, 149 149 (LAST_PKMAP*PAGE_SIZE) >> 10, 150 - FIXADDR_START, FIXADDR_TOP, 151 - (FIXADDR_TOP - FIXADDR_START) >> 10, 150 + FIXADDR_START, FIXADDR_END, 151 + (FIXADDR_END - FIXADDR_START) >> 10, 152 152 #endif 153 153 PAGE_OFFSET, PAGE_OFFSET + 154 154 (max_low_pfn - min_low_pfn) * PAGE_SIZE,
+2 -1
arch/xtensa/mm/mmu.c
··· 52 52 53 53 static void __init fixedrange_init(void) 54 54 { 55 - init_pmd(__fix_to_virt(0), __end_of_fixed_addresses); 55 + BUILD_BUG_ON(FIXADDR_START < TLBTEMP_BASE_1 + TLBTEMP_SIZE); 56 + init_pmd(FIXADDR_START, __end_of_fixed_addresses); 56 57 } 57 58 #endif 58 59