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

ARM: 6639/1: allow highmem on SMP platforms without h/w TLB ops broadcast

In commit e616c591405c168f6dc3dfd1221e105adfe49b8d, highmem support was
deactivated for SMP platforms without hardware TLB ops broadcast because
usage of kmap_high_get() requires that IRQs be disabled when kmap_lock
is locked which is incompatible with the IPI mechanism used by the
software TLB ops broadcast invoked through flush_all_zero_pkmaps().

The reason for kmap_high_get() is to ensure that the currently kmap'd
page usage count does not decrease to zero while we're using its
existing virtual mapping in an atomic context. With a VIVT cache this
is essential to do due to cache coherency issues, but with a VIPT cache
this is only an optimization so not to pay the price of establishing a
second mapping if an existing one can be used. However, on VIPT
platforms without hardware TLB maintenance we can give up on that
optimization in order to be able to use highmem.

From ARMv7 onwards the TLB ops are broadcasted in hardware, so let's
disable ARCH_NEEDS_KMAP_HIGH_GET only when CONFIG_SMP and
CONFIG_CPU_TLB_V6 are defined.

Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Tested-by: Saeed Bishara <saeed.bishara@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Nicolas Pitre and committed by
Russell King
aaa50048 c191789c

+28 -13
+28 -3
arch/arm/include/asm/highmem.h
··· 19 19 20 20 extern pte_t *pkmap_page_table; 21 21 22 - #define ARCH_NEEDS_KMAP_HIGH_GET 23 - 24 22 extern void *kmap_high(struct page *page); 25 - extern void *kmap_high_get(struct page *page); 26 23 extern void kunmap_high(struct page *page); 24 + 25 + /* 26 + * The reason for kmap_high_get() is to ensure that the currently kmap'd 27 + * page usage count does not decrease to zero while we're using its 28 + * existing virtual mapping in an atomic context. With a VIVT cache this 29 + * is essential to do, but with a VIPT cache this is only an optimization 30 + * so not to pay the price of establishing a second mapping if an existing 31 + * one can be used. However, on platforms without hardware TLB maintenance 32 + * broadcast, we simply cannot use ARCH_NEEDS_KMAP_HIGH_GET at all since 33 + * the locking involved must also disable IRQs which is incompatible with 34 + * the IPI mechanism used by global TLB operations. 35 + */ 36 + #define ARCH_NEEDS_KMAP_HIGH_GET 37 + #if defined(CONFIG_SMP) && defined(CONFIG_CPU_TLB_V6) 38 + #undef ARCH_NEEDS_KMAP_HIGH_GET 39 + #if defined(CONFIG_HIGHMEM) && defined(CONFIG_CPU_CACHE_VIVT) 40 + #error "The sum of features in your kernel config cannot be supported together" 41 + #endif 42 + #endif 43 + 44 + #ifdef ARCH_NEEDS_KMAP_HIGH_GET 45 + extern void *kmap_high_get(struct page *page); 46 + #else 47 + static inline void *kmap_high_get(struct page *page) 48 + { 49 + return NULL; 50 + } 51 + #endif 27 52 28 53 /* 29 54 * The following functions are already defined by <linux/highmem.h>
-10
arch/arm/mm/mmu.c
··· 827 827 * rather difficult. 828 828 */ 829 829 reason = "with VIPT aliasing cache"; 830 - } else if (is_smp() && tlb_ops_need_broadcast()) { 831 - /* 832 - * kmap_high needs to occasionally flush TLB entries, 833 - * however, if the TLB entries need to be broadcast 834 - * we may deadlock: 835 - * kmap_high(irqs off)->flush_all_zero_pkmaps-> 836 - * flush_tlb_kernel_range->smp_call_function_many 837 - * (must not be called with irqs off) 838 - */ 839 - reason = "without hardware TLB ops broadcasting"; 840 830 } 841 831 if (reason) { 842 832 printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memory\n",