Messages in this thread Patch in this message | | | From | Max Filippov <> | Subject | [PATCH] highmem: fix highmem for xtensa | Date | Fri, 13 Nov 2020 04:23:28 -0800 |
| |
Fixmap on xtensa grows upwards, i.e. bigger fixmap entry index corresponds to a higher virtual address. This was lost in highmem generalization resulting in the following runtime warnings:
WARNING: CPU: 0 PID: 18 at mm/highmem.c:494 kunmap_local_indexed+0x45/0x54 Modules linked in: CPU: 0 PID: 18 Comm: kworker/u2:0 Not tainted 5.10.0-rc3-next-20201113 #1 Call Trace: __warn+0x8f/0xc8 warn_slowpath_fmt+0x35/0x70 kunmap_local_indexed+0x45/0x54 handle_mm_fault+0x325/0xbe0 __get_user_pages.part.61+0x131/0x22c __get_user_pages+0x44/0x60 __get_user_pages_remote+0xe8/0x290 get_user_pages_remote+0x24/0x40 get_arg_page+0x50/0x78 copy_string_kernel+0x5c/0x120 kernel_execve+0x76/0xc8 call_usermodehelper_exec_async+0xc8/0x10c ret_from_kernel_thread+0xc/0x18
Fix it by adding __ARCH_HAS_POSITIVE_FIXMAP macro and implementing vaddr_in_fixmap and fixmap_pte primitives differently depending on whether it is defined or not.
Cc: Thomas Gleixner <tglx@linutronix.de> Fixes: 629ed3f7dad2 ("xtensa/mm/highmem: Switch to generic kmap atomic") Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> --- arch/xtensa/include/asm/fixmap.h | 2 ++ mm/highmem.c | 29 ++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/arch/xtensa/include/asm/fixmap.h b/arch/xtensa/include/asm/fixmap.h index 92049b61c351..66787b5f13d6 100644 --- a/arch/xtensa/include/asm/fixmap.h +++ b/arch/xtensa/include/asm/fixmap.h @@ -51,6 +51,8 @@ enum fixed_addresses { #define __fix_to_virt(x) (FIXADDR_START + ((x) << PAGE_SHIFT)) #define __virt_to_fix(x) (((x) - FIXADDR_START) >> PAGE_SHIFT) +#define __ARCH_HAS_POSITIVE_FIXMAP + #ifndef __ASSEMBLY__ /* * 'index to address' translation. If anyone tries to use the idx diff --git a/mm/highmem.c b/mm/highmem.c index 54bd233846c9..af27ed8d6a97 100644 --- a/mm/highmem.c +++ b/mm/highmem.c @@ -434,6 +434,26 @@ static inline void kmap_high_unmap_local(unsigned long vaddr) #endif } +static inline bool vaddr_in_fixmap(unsigned long addr) +{ +#ifdef __ARCH_HAS_POSITIVE_FIXMAP + return addr <= __fix_to_virt(FIX_KMAP_END) && + addr >= __fix_to_virt(FIX_KMAP_BEGIN); +#else + return addr >= __fix_to_virt(FIX_KMAP_END) && + addr <= __fix_to_virt(FIX_KMAP_BEGIN); +#endif +} + +static pte_t *fixmap_pte(pte_t *kmap_pte, int idx) +{ +#ifdef __ARCH_HAS_POSITIVE_FIXMAP + return kmap_pte + idx; +#else + return kmap_pte - idx; +#endif +} + static inline int kmap_local_calc_idx(int idx) { return idx + KM_MAX_IDX * smp_processor_id(); @@ -457,9 +477,9 @@ void *__kmap_local_pfn_prot(unsigned long pfn, pgprot_t prot) preempt_disable(); idx = arch_kmap_local_map_idx(kmap_local_idx_push(), pfn); vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); - BUG_ON(!pte_none(*(kmap_pte - idx))); + BUG_ON(!pte_none(*(fixmap_pte(kmap_pte, idx)))); pteval = pfn_pte(pfn, prot); - set_pte_at(&init_mm, vaddr, kmap_pte - idx, pteval); + set_pte_at(&init_mm, vaddr, fixmap_pte(kmap_pte, idx), pteval); arch_kmap_local_post_map(vaddr, pteval); preempt_enable(); @@ -489,8 +509,7 @@ void kunmap_local_indexed(void *vaddr) pte_t *kmap_pte = kmap_get_pte(); int idx; - if (addr < __fix_to_virt(FIX_KMAP_END) || - addr > __fix_to_virt(FIX_KMAP_BEGIN)) { + if (!vaddr_in_fixmap(addr)) { WARN_ON_ONCE(addr < PAGE_OFFSET); /* Handle mappings which were obtained by kmap_high_get() */ @@ -503,7 +522,7 @@ void kunmap_local_indexed(void *vaddr) WARN_ON_ONCE(addr != __fix_to_virt(FIX_KMAP_BEGIN + idx)); arch_kmap_local_pre_unmap(addr); - pte_clear(&init_mm, addr, kmap_pte - idx); + pte_clear(&init_mm, addr, fixmap_pte(kmap_pte, idx)); arch_kmap_local_post_unmap(addr); kmap_local_idx_pop(); preempt_enable(); -- 2.20.1
| |