diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-27 15:55:43 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-28 13:06:20 -0400 |
commit | e616c591405c168f6dc3dfd1221e105adfe49b8d (patch) | |
tree | e2e7642873628746aba95dd22d90da26935383ab | |
parent | 9a0f6b4646a0acaf7f06fd2e02f10f303fe85d8f (diff) |
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:
[<c00306f0>] (dump_backtrace+0x0/0x108) from [<c0286e6c>] (dump_stack+0x18/0x1c)
r6:c007cd18 r5:c02ff228 r4:0000017c
[<c0286e54>] (dump_stack+0x0/0x1c) from [<c0053e08>] (warn_slowpath_common+0x50/0x80)
[<c0053db8>] (warn_slowpath_common+0x0/0x80) from [<c0053e50>] (warn_slowpath_null+0x18/0x1c)
r7:00000003 r6:00000001 r5:c1ff4000 r4:c035fa34
[<c0053e38>] (warn_slowpath_null+0x0/0x1c) from [<c007cd18>] (smp_call_function_many+0xc4/0x240)
[<c007cc54>] (smp_call_function_many+0x0/0x240) from [<c007cec0>] (smp_call_function+0x2c/0x38)
[<c007ce94>] (smp_call_function+0x0/0x38) from [<c005980c>] (on_each_cpu+0x1c/0x38)
[<c00597f0>] (on_each_cpu+0x0/0x38) from [<c0031788>] (flush_tlb_kernel_range+0x50/0x58)
r6:00000001 r5:00000800 r4:c05f3590
[<c0031738>] (flush_tlb_kernel_range+0x0/0x58) from [<c009c600>] (flush_all_zero_pkmaps+0xc0/0xe8)
[<c009c540>] (flush_all_zero_pkmaps+0x0/0xe8) from [<c009c6b4>] (kmap_high+0x8c/0x1e0)
[<c009c628>] (kmap_high+0x0/0x1e0) from [<c00364a8>] (kmap+0x44/0x5c)
[<c0036464>] (kmap+0x0/0x5c) from [<c0109dfc>] (cramfs_readpage+0x3c/0x194)
[<c0109dc0>] (cramfs_readpage+0x0/0x194) from [<c0090c14>] (__do_page_cache_readahead+0x1f0/0x290)
[<c0090a24>] (__do_page_cache_readahead+0x0/0x290) from [<c0090ce4>] (ra_submit+0x30/0x38)
[<c0090cb4>] (ra_submit+0x0/0x38) from [<c0089384>] (filemap_fault+0x3dc/0x438)
r4:c1819988
[<c0088fa8>] (filemap_fault+0x0/0x438) from [<c009d21c>] (__do_fault+0x58/0x43c)
[<c009d1c4>] (__do_fault+0x0/0x43c) from [<c009e8cc>] (handle_mm_fault+0x104/0x318)
[<c009e7c8>] (handle_mm_fault+0x0/0x318) from [<c0033c98>] (do_page_fault+0x188/0x1e4)
[<c0033b10>] (do_page_fault+0x0/0x1e4) from [<c0033ddc>] (do_translation_fault+0x7c/0x84)
[<c0033d60>] (do_translation_fault+0x0/0x84) from [<c002b474>] (do_DataAbort+0x40/0xa4)
r8:c1ff5e20 r7:c0340120 r6:00000805 r5:c1ff5e54 r4:c03400d0
[<c002b434>] (do_DataAbort+0x0/0xa4) from [<c002bcac>] (__dabt_svc+0x4c/0x60)
...
So we disable highmem support on these systems.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/include/asm/smp_plat.h | 16 | ||||
-rw-r--r-- | arch/arm/kernel/smp.c | 7 | ||||
-rw-r--r-- | arch/arm/mm/mmu.c | 37 |
3 files changed, 50 insertions, 10 deletions
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 @@ | |||
1 | /* | ||
2 | * ARM specific SMP header, this contains our implementation | ||
3 | * details. | ||
4 | */ | ||
5 | #ifndef __ASMARM_SMP_PLAT_H | ||
6 | #define __ASMARM_SMP_PLAT_H | ||
7 | |||
8 | #include <asm/cputype.h> | ||
9 | |||
10 | /* all SMP configurations have the extended CPUID registers */ | ||
11 | static inline int tlb_ops_need_broadcast(void) | ||
12 | { | ||
13 | return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; | ||
14 | } | ||
15 | |||
16 | #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 @@ | |||
36 | #include <asm/tlbflush.h> | 36 | #include <asm/tlbflush.h> |
37 | #include <asm/ptrace.h> | 37 | #include <asm/ptrace.h> |
38 | #include <asm/localtimer.h> | 38 | #include <asm/localtimer.h> |
39 | #include <asm/smp_plat.h> | ||
39 | 40 | ||
40 | /* | 41 | /* |
41 | * as from 2.5, kernels no longer have an init_tasks structure | 42 | * as from 2.5, kernels no longer have an init_tasks structure |
@@ -586,12 +587,6 @@ struct tlb_args { | |||
586 | unsigned long ta_end; | 587 | unsigned long ta_end; |
587 | }; | 588 | }; |
588 | 589 | ||
589 | /* all SMP configurations have the extended CPUID registers */ | ||
590 | static inline int tlb_ops_need_broadcast(void) | ||
591 | { | ||
592 | return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; | ||
593 | } | ||
594 | |||
595 | static inline void ipi_flush_tlb_all(void *ignored) | 590 | static inline void ipi_flush_tlb_all(void *ignored) |
596 | { | 591 | { |
597 | local_flush_tlb_all(); | 592 | 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 @@ | |||
21 | #include <asm/cachetype.h> | 21 | #include <asm/cachetype.h> |
22 | #include <asm/setup.h> | 22 | #include <asm/setup.h> |
23 | #include <asm/sizes.h> | 23 | #include <asm/sizes.h> |
24 | #include <asm/smp_plat.h> | ||
24 | #include <asm/tlb.h> | 25 | #include <asm/tlb.h> |
25 | #include <asm/highmem.h> | 26 | #include <asm/highmem.h> |
26 | 27 | ||
@@ -709,10 +710,6 @@ static void __init sanity_check_meminfo(void) | |||
709 | if (meminfo.nr_banks >= NR_BANKS) { | 710 | if (meminfo.nr_banks >= NR_BANKS) { |
710 | printk(KERN_CRIT "NR_BANKS too low, " | 711 | printk(KERN_CRIT "NR_BANKS too low, " |
711 | "ignoring high memory\n"); | 712 | "ignoring high memory\n"); |
712 | } else if (cache_is_vipt_aliasing()) { | ||
713 | printk(KERN_CRIT "HIGHMEM is not yet supported " | ||
714 | "with VIPT aliasing cache, " | ||
715 | "ignoring high memory\n"); | ||
716 | } else { | 713 | } else { |
717 | memmove(bank + 1, bank, | 714 | memmove(bank + 1, bank, |
718 | (meminfo.nr_banks - i) * sizeof(*bank)); | 715 | (meminfo.nr_banks - i) * sizeof(*bank)); |
@@ -756,6 +753,38 @@ static void __init sanity_check_meminfo(void) | |||
756 | #endif | 753 | #endif |
757 | j++; | 754 | j++; |
758 | } | 755 | } |
756 | #ifdef CONFIG_HIGHMEM | ||
757 | if (highmem) { | ||
758 | const char *reason = NULL; | ||
759 | |||
760 | if (cache_is_vipt_aliasing()) { | ||
761 | /* | ||
762 | * Interactions between kmap and other mappings | ||
763 | * make highmem support with aliasing VIPT caches | ||
764 | * rather difficult. | ||
765 | */ | ||
766 | reason = "with VIPT aliasing cache"; | ||
767 | #ifdef CONFIG_SMP | ||
768 | } else if (tlb_ops_need_broadcast()) { | ||
769 | /* | ||
770 | * kmap_high needs to occasionally flush TLB entries, | ||
771 | * however, if the TLB entries need to be broadcast | ||
772 | * we may deadlock: | ||
773 | * kmap_high(irqs off)->flush_all_zero_pkmaps-> | ||
774 | * flush_tlb_kernel_range->smp_call_function_many | ||
775 | * (must not be called with irqs off) | ||
776 | */ | ||
777 | reason = "without hardware TLB ops broadcasting"; | ||
778 | #endif | ||
779 | } | ||
780 | if (reason) { | ||
781 | printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memory\n", | ||
782 | reason); | ||
783 | while (j > 0 && meminfo.bank[j - 1].highmem) | ||
784 | j--; | ||
785 | } | ||
786 | } | ||
787 | #endif | ||
759 | meminfo.nr_banks = j; | 788 | meminfo.nr_banks = j; |
760 | } | 789 | } |
761 | 790 | ||