From e616c591405c168f6dc3dfd1221e105adfe49b8d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 27 Sep 2009 20:55:43 +0100 Subject: [PATCH] ARM: Don't allow highmem on SMP platforms without h/w TLB ops broadcast We suffer an unfortunate combination of "features" which makes highmem support on platforms without hardware TLB maintainence broadcast difficult: - we need kmap_high_get() support for DMA cache coherence - this requires kmap_high() to take a spinlock with IRQs disabled - kmap_high() occasionally calls flush_all_zero_pkmaps() to clear out old mappings - flush_all_zero_pkmaps() calls flush_tlb_kernel_range(), which on s/w IPI'd systems eventually calls smp_call_function_many() - smp_call_function_many() must not be called with IRQs disabled: WARNING: at kernel/smp.c:380 smp_call_function_many+0xc4/0x240() Modules linked in: Backtrace: [] (dump_backtrace+0x0/0x108) from [] (dump_stack+0x18/0x1c) r6:c007cd18 r5:c02ff228 r4:0000017c [] (dump_stack+0x0/0x1c) from [] (warn_slowpath_common+0x50/0x80) [] (warn_slowpath_common+0x0/0x80) from [] (warn_slowpath_null+0x18/0x1c) r7:00000003 r6:00000001 r5:c1ff4000 r4:c035fa34 [] (warn_slowpath_null+0x0/0x1c) from [] (smp_call_function_many+0xc4/0x240) [] (smp_call_function_many+0x0/0x240) from [] (smp_call_function+0x2c/0x38) [] (smp_call_function+0x0/0x38) from [] (on_each_cpu+0x1c/0x38) [] (on_each_cpu+0x0/0x38) from [] (flush_tlb_kernel_range+0x50/0x58) r6:00000001 r5:00000800 r4:c05f3590 [] (flush_tlb_kernel_range+0x0/0x58) from [] (flush_all_zero_pkmaps+0xc0/0xe8) [] (flush_all_zero_pkmaps+0x0/0xe8) from [] (kmap_high+0x8c/0x1e0) [] (kmap_high+0x0/0x1e0) from [] (kmap+0x44/0x5c) [] (kmap+0x0/0x5c) from [] (cramfs_readpage+0x3c/0x194) [] (cramfs_readpage+0x0/0x194) from [] (__do_page_cache_readahead+0x1f0/0x290) [] (__do_page_cache_readahead+0x0/0x290) from [] (ra_submit+0x30/0x38) [] (ra_submit+0x0/0x38) from [] (filemap_fault+0x3dc/0x438) r4:c1819988 [] (filemap_fault+0x0/0x438) from [] (__do_fault+0x58/0x43c) [] (__do_fault+0x0/0x43c) from [] (handle_mm_fault+0x104/0x318) [] (handle_mm_fault+0x0/0x318) from [] (do_page_fault+0x188/0x1e4) [] (do_page_fault+0x0/0x1e4) from [] (do_translation_fault+0x7c/0x84) [] (do_translation_fault+0x0/0x84) from [] (do_DataAbort+0x40/0xa4) r8:c1ff5e20 r7:c0340120 r6:00000805 r5:c1ff5e54 r4:c03400d0 [] (do_DataAbort+0x0/0xa4) from [] (__dabt_svc+0x4c/0x60) ... So we disable highmem support on these systems. Signed-off-by: Russell King --- arch/arm/include/asm/smp_plat.h | 16 ++++++++++++++ arch/arm/kernel/smp.c | 7 +------ arch/arm/mm/mmu.c | 37 +++++++++++++++++++++++++++++---- 3 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 arch/arm/include/asm/smp_plat.h diff --git a/arch/arm/include/asm/smp_plat.h b/arch/arm/include/asm/smp_plat.h new file mode 100644 index 000000000000..59303e200845 --- /dev/null +++ b/arch/arm/include/asm/smp_plat.h @@ -0,0 +1,16 @@ +/* + * ARM specific SMP header, this contains our implementation + * details. + */ +#ifndef __ASMARM_SMP_PLAT_H +#define __ASMARM_SMP_PLAT_H + +#include + +/* all SMP configurations have the extended CPUID registers */ +static inline int tlb_ops_need_broadcast(void) +{ + return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; +} + +#endif diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index e0d32770bb3d..9d015ee5747a 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -36,6 +36,7 @@ #include #include #include +#include /* * as from 2.5, kernels no longer have an init_tasks structure @@ -586,12 +587,6 @@ struct tlb_args { unsigned long ta_end; }; -/* all SMP configurations have the extended CPUID registers */ -static inline int tlb_ops_need_broadcast(void) -{ - return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; -} - static inline void ipi_flush_tlb_all(void *ignored) { local_flush_tlb_all(); diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index ce551ec2cb23..02243eeccf50 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -709,10 +710,6 @@ static void __init sanity_check_meminfo(void) if (meminfo.nr_banks >= NR_BANKS) { printk(KERN_CRIT "NR_BANKS too low, " "ignoring high memory\n"); - } else if (cache_is_vipt_aliasing()) { - printk(KERN_CRIT "HIGHMEM is not yet supported " - "with VIPT aliasing cache, " - "ignoring high memory\n"); } else { memmove(bank + 1, bank, (meminfo.nr_banks - i) * sizeof(*bank)); @@ -756,6 +753,38 @@ static void __init sanity_check_meminfo(void) #endif j++; } +#ifdef CONFIG_HIGHMEM + if (highmem) { + const char *reason = NULL; + + if (cache_is_vipt_aliasing()) { + /* + * Interactions between kmap and other mappings + * make highmem support with aliasing VIPT caches + * rather difficult. + */ + reason = "with VIPT aliasing cache"; +#ifdef CONFIG_SMP + } else if (tlb_ops_need_broadcast()) { + /* + * kmap_high needs to occasionally flush TLB entries, + * however, if the TLB entries need to be broadcast + * we may deadlock: + * kmap_high(irqs off)->flush_all_zero_pkmaps-> + * flush_tlb_kernel_range->smp_call_function_many + * (must not be called with irqs off) + */ + reason = "without hardware TLB ops broadcasting"; +#endif + } + if (reason) { + printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memory\n", + reason); + while (j > 0 && meminfo.bank[j - 1].highmem) + j--; + } + } +#endif meminfo.nr_banks = j; }