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

mm/highmem: Take kmap_high_get() properly into account

kunmap_local() warns when the virtual address to unmap is below
PAGE_OFFSET. This is correct except for the case that the mapping was
obtained via kmap_high_get() because the PKMAP addresses are right below
PAGE_OFFSET.

Cure it by skipping the WARN_ON() when the unmap was handled by
kunmap_high().

Fixes: 298fa1ad5571 ("highmem: Provide generic variant of kmap_atomic*")
Reported-by: vtolkm@googlemail.com
Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Link: https://lore.kernel.org/r/87y2j6n8mj.fsf@nanos.tec.linutronix.de

+13 -6
+13 -6
mm/highmem.c
··· 426 426 #endif 427 427 428 428 /* Unmap a local mapping which was obtained by kmap_high_get() */ 429 - static inline void kmap_high_unmap_local(unsigned long vaddr) 429 + static inline bool kmap_high_unmap_local(unsigned long vaddr) 430 430 { 431 431 #ifdef ARCH_NEEDS_KMAP_HIGH_GET 432 - if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) 432 + if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) { 433 433 kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)])); 434 + return true; 435 + } 434 436 #endif 437 + return false; 435 438 } 436 439 437 440 static inline int kmap_local_calc_idx(int idx) ··· 494 491 495 492 if (addr < __fix_to_virt(FIX_KMAP_END) || 496 493 addr > __fix_to_virt(FIX_KMAP_BEGIN)) { 497 - WARN_ON_ONCE(addr < PAGE_OFFSET); 498 - 499 - /* Handle mappings which were obtained by kmap_high_get() */ 500 - kmap_high_unmap_local(addr); 494 + /* 495 + * Handle mappings which were obtained by kmap_high_get() 496 + * first as the virtual address of such mappings is below 497 + * PAGE_OFFSET. Warn for all other addresses which are in 498 + * the user space part of the virtual address space. 499 + */ 500 + if (!kmap_high_unmap_local(addr)) 501 + WARN_ON_ONCE(addr < PAGE_OFFSET); 501 502 return; 502 503 } 503 504