diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-12-11 05:01:53 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-12-11 05:01:53 -0500 |
commit | 0fa5d3996dbda1ee9653c43d39b7ef159fb57ee7 (patch) | |
tree | 70f0adc3b86bb1511be6607c959506f6365fc2a9 /arch/arm/mm | |
parent | 0b99cb73105f0527c1c4096960796b8772343a39 (diff) | |
parent | 14318efb322e2fe1a034c69463d725209eb9d548 (diff) |
Merge branch 'devel-stable' into for-linus
Diffstat (limited to 'arch/arm/mm')
-rw-r--r-- | arch/arm/mm/context.c | 207 | ||||
-rw-r--r-- | arch/arm/mm/ioremap.c | 16 | ||||
-rw-r--r-- | arch/arm/mm/mmu.c | 2 | ||||
-rw-r--r-- | arch/arm/mm/proc-macros.S | 4 | ||||
-rw-r--r-- | arch/arm/mm/proc-v7-2level.S | 10 | ||||
-rw-r--r-- | arch/arm/mm/proc-v7-3level.S | 5 |
6 files changed, 132 insertions, 112 deletions
diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c index 4e07eec1270d..bc4a5e9ebb78 100644 --- a/arch/arm/mm/context.c +++ b/arch/arm/mm/context.c | |||
@@ -2,6 +2,9 @@ | |||
2 | * linux/arch/arm/mm/context.c | 2 | * linux/arch/arm/mm/context.c |
3 | * | 3 | * |
4 | * Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved. | 4 | * Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved. |
5 | * Copyright (C) 2012 ARM Limited | ||
6 | * | ||
7 | * Author: Will Deacon <will.deacon@arm.com> | ||
5 | * | 8 | * |
6 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
@@ -14,14 +17,40 @@ | |||
14 | #include <linux/percpu.h> | 17 | #include <linux/percpu.h> |
15 | 18 | ||
16 | #include <asm/mmu_context.h> | 19 | #include <asm/mmu_context.h> |
20 | #include <asm/smp_plat.h> | ||
17 | #include <asm/thread_notify.h> | 21 | #include <asm/thread_notify.h> |
18 | #include <asm/tlbflush.h> | 22 | #include <asm/tlbflush.h> |
19 | 23 | ||
24 | /* | ||
25 | * On ARMv6, we have the following structure in the Context ID: | ||
26 | * | ||
27 | * 31 7 0 | ||
28 | * +-------------------------+-----------+ | ||
29 | * | process ID | ASID | | ||
30 | * +-------------------------+-----------+ | ||
31 | * | context ID | | ||
32 | * +-------------------------------------+ | ||
33 | * | ||
34 | * The ASID is used to tag entries in the CPU caches and TLBs. | ||
35 | * The context ID is used by debuggers and trace logic, and | ||
36 | * should be unique within all running processes. | ||
37 | */ | ||
38 | #define ASID_FIRST_VERSION (1ULL << ASID_BITS) | ||
39 | #define NUM_USER_ASIDS (ASID_FIRST_VERSION - 1) | ||
40 | |||
41 | #define ASID_TO_IDX(asid) ((asid & ~ASID_MASK) - 1) | ||
42 | #define IDX_TO_ASID(idx) ((idx + 1) & ~ASID_MASK) | ||
43 | |||
20 | static DEFINE_RAW_SPINLOCK(cpu_asid_lock); | 44 | static DEFINE_RAW_SPINLOCK(cpu_asid_lock); |
21 | unsigned int cpu_last_asid = ASID_FIRST_VERSION; | 45 | static atomic64_t asid_generation = ATOMIC64_INIT(ASID_FIRST_VERSION); |
46 | static DECLARE_BITMAP(asid_map, NUM_USER_ASIDS); | ||
47 | |||
48 | static DEFINE_PER_CPU(atomic64_t, active_asids); | ||
49 | static DEFINE_PER_CPU(u64, reserved_asids); | ||
50 | static cpumask_t tlb_flush_pending; | ||
22 | 51 | ||
23 | #ifdef CONFIG_ARM_LPAE | 52 | #ifdef CONFIG_ARM_LPAE |
24 | void cpu_set_reserved_ttbr0(void) | 53 | static void cpu_set_reserved_ttbr0(void) |
25 | { | 54 | { |
26 | unsigned long ttbl = __pa(swapper_pg_dir); | 55 | unsigned long ttbl = __pa(swapper_pg_dir); |
27 | unsigned long ttbh = 0; | 56 | unsigned long ttbh = 0; |
@@ -37,7 +66,7 @@ void cpu_set_reserved_ttbr0(void) | |||
37 | isb(); | 66 | isb(); |
38 | } | 67 | } |
39 | #else | 68 | #else |
40 | void cpu_set_reserved_ttbr0(void) | 69 | static void cpu_set_reserved_ttbr0(void) |
41 | { | 70 | { |
42 | u32 ttb; | 71 | u32 ttb; |
43 | /* Copy TTBR1 into TTBR0 */ | 72 | /* Copy TTBR1 into TTBR0 */ |
@@ -84,124 +113,104 @@ static int __init contextidr_notifier_init(void) | |||
84 | arch_initcall(contextidr_notifier_init); | 113 | arch_initcall(contextidr_notifier_init); |
85 | #endif | 114 | #endif |
86 | 115 | ||
87 | /* | 116 | static void flush_context(unsigned int cpu) |
88 | * We fork()ed a process, and we need a new context for the child | ||
89 | * to run in. | ||
90 | */ | ||
91 | void __init_new_context(struct task_struct *tsk, struct mm_struct *mm) | ||
92 | { | 117 | { |
93 | mm->context.id = 0; | 118 | int i; |
94 | raw_spin_lock_init(&mm->context.id_lock); | 119 | u64 asid; |
95 | } | 120 | |
121 | /* Update the list of reserved ASIDs and the ASID bitmap. */ | ||
122 | bitmap_clear(asid_map, 0, NUM_USER_ASIDS); | ||
123 | for_each_possible_cpu(i) { | ||
124 | if (i == cpu) { | ||
125 | asid = 0; | ||
126 | } else { | ||
127 | asid = atomic64_xchg(&per_cpu(active_asids, i), 0); | ||
128 | __set_bit(ASID_TO_IDX(asid), asid_map); | ||
129 | } | ||
130 | per_cpu(reserved_asids, i) = asid; | ||
131 | } | ||
96 | 132 | ||
97 | static void flush_context(void) | 133 | /* Queue a TLB invalidate and flush the I-cache if necessary. */ |
98 | { | 134 | if (!tlb_ops_need_broadcast()) |
99 | cpu_set_reserved_ttbr0(); | 135 | cpumask_set_cpu(cpu, &tlb_flush_pending); |
100 | local_flush_tlb_all(); | 136 | else |
101 | if (icache_is_vivt_asid_tagged()) { | 137 | cpumask_setall(&tlb_flush_pending); |
138 | |||
139 | if (icache_is_vivt_asid_tagged()) | ||
102 | __flush_icache_all(); | 140 | __flush_icache_all(); |
103 | dsb(); | ||
104 | } | ||
105 | } | 141 | } |
106 | 142 | ||
107 | #ifdef CONFIG_SMP | 143 | static int is_reserved_asid(u64 asid) |
144 | { | ||
145 | int cpu; | ||
146 | for_each_possible_cpu(cpu) | ||
147 | if (per_cpu(reserved_asids, cpu) == asid) | ||
148 | return 1; | ||
149 | return 0; | ||
150 | } | ||
108 | 151 | ||
109 | static void set_mm_context(struct mm_struct *mm, unsigned int asid) | 152 | static void new_context(struct mm_struct *mm, unsigned int cpu) |
110 | { | 153 | { |
111 | unsigned long flags; | 154 | u64 asid = mm->context.id; |
155 | u64 generation = atomic64_read(&asid_generation); | ||
112 | 156 | ||
113 | /* | 157 | if (asid != 0 && is_reserved_asid(asid)) { |
114 | * Locking needed for multi-threaded applications where the | ||
115 | * same mm->context.id could be set from different CPUs during | ||
116 | * the broadcast. This function is also called via IPI so the | ||
117 | * mm->context.id_lock has to be IRQ-safe. | ||
118 | */ | ||
119 | raw_spin_lock_irqsave(&mm->context.id_lock, flags); | ||
120 | if (likely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) { | ||
121 | /* | 158 | /* |
122 | * Old version of ASID found. Set the new one and | 159 | * Our current ASID was active during a rollover, we can |
123 | * reset mm_cpumask(mm). | 160 | * continue to use it and this was just a false alarm. |
124 | */ | 161 | */ |
125 | mm->context.id = asid; | 162 | asid = generation | (asid & ~ASID_MASK); |
163 | } else { | ||
164 | /* | ||
165 | * Allocate a free ASID. If we can't find one, take a | ||
166 | * note of the currently active ASIDs and mark the TLBs | ||
167 | * as requiring flushes. | ||
168 | */ | ||
169 | asid = find_first_zero_bit(asid_map, NUM_USER_ASIDS); | ||
170 | if (asid == NUM_USER_ASIDS) { | ||
171 | generation = atomic64_add_return(ASID_FIRST_VERSION, | ||
172 | &asid_generation); | ||
173 | flush_context(cpu); | ||
174 | asid = find_first_zero_bit(asid_map, NUM_USER_ASIDS); | ||
175 | } | ||
176 | __set_bit(asid, asid_map); | ||
177 | asid = generation | IDX_TO_ASID(asid); | ||
126 | cpumask_clear(mm_cpumask(mm)); | 178 | cpumask_clear(mm_cpumask(mm)); |
127 | } | 179 | } |
128 | raw_spin_unlock_irqrestore(&mm->context.id_lock, flags); | ||
129 | 180 | ||
130 | /* | 181 | mm->context.id = asid; |
131 | * Set the mm_cpumask(mm) bit for the current CPU. | ||
132 | */ | ||
133 | cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm)); | ||
134 | } | 182 | } |
135 | 183 | ||
136 | /* | 184 | void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk) |
137 | * Reset the ASID on the current CPU. This function call is broadcast | ||
138 | * from the CPU handling the ASID rollover and holding cpu_asid_lock. | ||
139 | */ | ||
140 | static void reset_context(void *info) | ||
141 | { | 185 | { |
142 | unsigned int asid; | 186 | unsigned long flags; |
143 | unsigned int cpu = smp_processor_id(); | 187 | unsigned int cpu = smp_processor_id(); |
144 | struct mm_struct *mm = current->active_mm; | ||
145 | 188 | ||
146 | smp_rmb(); | 189 | if (unlikely(mm->context.vmalloc_seq != init_mm.context.vmalloc_seq)) |
147 | asid = cpu_last_asid + cpu + 1; | 190 | __check_vmalloc_seq(mm); |
148 | 191 | ||
149 | flush_context(); | 192 | /* |
150 | set_mm_context(mm, asid); | 193 | * Required during context switch to avoid speculative page table |
151 | 194 | * walking with the wrong TTBR. | |
152 | /* set the new ASID */ | 195 | */ |
153 | cpu_switch_mm(mm->pgd, mm); | 196 | cpu_set_reserved_ttbr0(); |
154 | } | ||
155 | 197 | ||
156 | #else | 198 | if (!((mm->context.id ^ atomic64_read(&asid_generation)) >> ASID_BITS) |
199 | && atomic64_xchg(&per_cpu(active_asids, cpu), mm->context.id)) | ||
200 | goto switch_mm_fastpath; | ||
157 | 201 | ||
158 | static inline void set_mm_context(struct mm_struct *mm, unsigned int asid) | 202 | raw_spin_lock_irqsave(&cpu_asid_lock, flags); |
159 | { | 203 | /* Check that our ASID belongs to the current generation. */ |
160 | mm->context.id = asid; | 204 | if ((mm->context.id ^ atomic64_read(&asid_generation)) >> ASID_BITS) |
161 | cpumask_copy(mm_cpumask(mm), cpumask_of(smp_processor_id())); | 205 | new_context(mm, cpu); |
162 | } | ||
163 | 206 | ||
164 | #endif | 207 | atomic64_set(&per_cpu(active_asids, cpu), mm->context.id); |
208 | cpumask_set_cpu(cpu, mm_cpumask(mm)); | ||
165 | 209 | ||
166 | void __new_context(struct mm_struct *mm) | 210 | if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending)) |
167 | { | 211 | local_flush_tlb_all(); |
168 | unsigned int asid; | 212 | raw_spin_unlock_irqrestore(&cpu_asid_lock, flags); |
169 | 213 | ||
170 | raw_spin_lock(&cpu_asid_lock); | 214 | switch_mm_fastpath: |
171 | #ifdef CONFIG_SMP | 215 | cpu_switch_mm(mm->pgd, mm); |
172 | /* | ||
173 | * Check the ASID again, in case the change was broadcast from | ||
174 | * another CPU before we acquired the lock. | ||
175 | */ | ||
176 | if (unlikely(((mm->context.id ^ cpu_last_asid) >> ASID_BITS) == 0)) { | ||
177 | cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm)); | ||
178 | raw_spin_unlock(&cpu_asid_lock); | ||
179 | return; | ||
180 | } | ||
181 | #endif | ||
182 | /* | ||
183 | * At this point, it is guaranteed that the current mm (with | ||
184 | * an old ASID) isn't active on any other CPU since the ASIDs | ||
185 | * are changed simultaneously via IPI. | ||
186 | */ | ||
187 | asid = ++cpu_last_asid; | ||
188 | if (asid == 0) | ||
189 | asid = cpu_last_asid = ASID_FIRST_VERSION; | ||
190 | |||
191 | /* | ||
192 | * If we've used up all our ASIDs, we need | ||
193 | * to start a new version and flush the TLB. | ||
194 | */ | ||
195 | if (unlikely((asid & ~ASID_MASK) == 0)) { | ||
196 | asid = cpu_last_asid + smp_processor_id() + 1; | ||
197 | flush_context(); | ||
198 | #ifdef CONFIG_SMP | ||
199 | smp_wmb(); | ||
200 | smp_call_function(reset_context, NULL, 1); | ||
201 | #endif | ||
202 | cpu_last_asid += NR_CPUS; | ||
203 | } | ||
204 | |||
205 | set_mm_context(mm, asid); | ||
206 | raw_spin_unlock(&cpu_asid_lock); | ||
207 | } | 216 | } |
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index 5dcc2fd46c46..88fd86cf3d9a 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c | |||
@@ -47,18 +47,18 @@ int ioremap_page(unsigned long virt, unsigned long phys, | |||
47 | } | 47 | } |
48 | EXPORT_SYMBOL(ioremap_page); | 48 | EXPORT_SYMBOL(ioremap_page); |
49 | 49 | ||
50 | void __check_kvm_seq(struct mm_struct *mm) | 50 | void __check_vmalloc_seq(struct mm_struct *mm) |
51 | { | 51 | { |
52 | unsigned int seq; | 52 | unsigned int seq; |
53 | 53 | ||
54 | do { | 54 | do { |
55 | seq = init_mm.context.kvm_seq; | 55 | seq = init_mm.context.vmalloc_seq; |
56 | memcpy(pgd_offset(mm, VMALLOC_START), | 56 | memcpy(pgd_offset(mm, VMALLOC_START), |
57 | pgd_offset_k(VMALLOC_START), | 57 | pgd_offset_k(VMALLOC_START), |
58 | sizeof(pgd_t) * (pgd_index(VMALLOC_END) - | 58 | sizeof(pgd_t) * (pgd_index(VMALLOC_END) - |
59 | pgd_index(VMALLOC_START))); | 59 | pgd_index(VMALLOC_START))); |
60 | mm->context.kvm_seq = seq; | 60 | mm->context.vmalloc_seq = seq; |
61 | } while (seq != init_mm.context.kvm_seq); | 61 | } while (seq != init_mm.context.vmalloc_seq); |
62 | } | 62 | } |
63 | 63 | ||
64 | #if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE) | 64 | #if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE) |
@@ -89,13 +89,13 @@ static void unmap_area_sections(unsigned long virt, unsigned long size) | |||
89 | if (!pmd_none(pmd)) { | 89 | if (!pmd_none(pmd)) { |
90 | /* | 90 | /* |
91 | * Clear the PMD from the page table, and | 91 | * Clear the PMD from the page table, and |
92 | * increment the kvm sequence so others | 92 | * increment the vmalloc sequence so others |
93 | * notice this change. | 93 | * notice this change. |
94 | * | 94 | * |
95 | * Note: this is still racy on SMP machines. | 95 | * Note: this is still racy on SMP machines. |
96 | */ | 96 | */ |
97 | pmd_clear(pmdp); | 97 | pmd_clear(pmdp); |
98 | init_mm.context.kvm_seq++; | 98 | init_mm.context.vmalloc_seq++; |
99 | 99 | ||
100 | /* | 100 | /* |
101 | * Free the page table, if there was one. | 101 | * Free the page table, if there was one. |
@@ -112,8 +112,8 @@ static void unmap_area_sections(unsigned long virt, unsigned long size) | |||
112 | * Ensure that the active_mm is up to date - we want to | 112 | * Ensure that the active_mm is up to date - we want to |
113 | * catch any use-after-iounmap cases. | 113 | * catch any use-after-iounmap cases. |
114 | */ | 114 | */ |
115 | if (current->active_mm->context.kvm_seq != init_mm.context.kvm_seq) | 115 | if (current->active_mm->context.vmalloc_seq != init_mm.context.vmalloc_seq) |
116 | __check_kvm_seq(current->active_mm); | 116 | __check_vmalloc_seq(current->active_mm); |
117 | 117 | ||
118 | flush_tlb_kernel_range(virt, end); | 118 | flush_tlb_kernel_range(virt, end); |
119 | } | 119 | } |
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 941dfb9e9a78..99b47b950efc 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c | |||
@@ -488,7 +488,7 @@ static void __init build_mem_type_table(void) | |||
488 | #endif | 488 | #endif |
489 | 489 | ||
490 | for (i = 0; i < 16; i++) { | 490 | for (i = 0; i < 16; i++) { |
491 | unsigned long v = pgprot_val(protection_map[i]); | 491 | pteval_t v = pgprot_val(protection_map[i]); |
492 | protection_map[i] = __pgprot(v | user_pgprot); | 492 | protection_map[i] = __pgprot(v | user_pgprot); |
493 | } | 493 | } |
494 | 494 | ||
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S index b29a2265af01..eb6aa73bc8b7 100644 --- a/arch/arm/mm/proc-macros.S +++ b/arch/arm/mm/proc-macros.S | |||
@@ -167,6 +167,10 @@ | |||
167 | tst r1, #L_PTE_YOUNG | 167 | tst r1, #L_PTE_YOUNG |
168 | tstne r1, #L_PTE_PRESENT | 168 | tstne r1, #L_PTE_PRESENT |
169 | moveq r3, #0 | 169 | moveq r3, #0 |
170 | #ifndef CONFIG_CPU_USE_DOMAINS | ||
171 | tstne r1, #L_PTE_NONE | ||
172 | movne r3, #0 | ||
173 | #endif | ||
170 | 174 | ||
171 | str r3, [r0] | 175 | str r3, [r0] |
172 | mcr p15, 0, r0, c7, c10, 1 @ flush_pte | 176 | mcr p15, 0, r0, c7, c10, 1 @ flush_pte |
diff --git a/arch/arm/mm/proc-v7-2level.S b/arch/arm/mm/proc-v7-2level.S index fd045e706390..6d98c13ab827 100644 --- a/arch/arm/mm/proc-v7-2level.S +++ b/arch/arm/mm/proc-v7-2level.S | |||
@@ -100,7 +100,11 @@ ENTRY(cpu_v7_set_pte_ext) | |||
100 | orrne r3, r3, #PTE_EXT_XN | 100 | orrne r3, r3, #PTE_EXT_XN |
101 | 101 | ||
102 | tst r1, #L_PTE_YOUNG | 102 | tst r1, #L_PTE_YOUNG |
103 | tstne r1, #L_PTE_PRESENT | 103 | tstne r1, #L_PTE_VALID |
104 | #ifndef CONFIG_CPU_USE_DOMAINS | ||
105 | eorne r1, r1, #L_PTE_NONE | ||
106 | tstne r1, #L_PTE_NONE | ||
107 | #endif | ||
104 | moveq r3, #0 | 108 | moveq r3, #0 |
105 | 109 | ||
106 | ARM( str r3, [r0, #2048]! ) | 110 | ARM( str r3, [r0, #2048]! ) |
@@ -161,11 +165,11 @@ ENDPROC(cpu_v7_set_pte_ext) | |||
161 | * TFR EV X F I D LR S | 165 | * TFR EV X F I D LR S |
162 | * .EEE ..EE PUI. .T.T 4RVI ZWRS BLDP WCAM | 166 | * .EEE ..EE PUI. .T.T 4RVI ZWRS BLDP WCAM |
163 | * rxxx rrxx xxx0 0101 xxxx xxxx x111 xxxx < forced | 167 | * rxxx rrxx xxx0 0101 xxxx xxxx x111 xxxx < forced |
164 | * 1 0 110 0011 1100 .111 1101 < we want | 168 | * 01 0 110 0011 1100 .111 1101 < we want |
165 | */ | 169 | */ |
166 | .align 2 | 170 | .align 2 |
167 | .type v7_crval, #object | 171 | .type v7_crval, #object |
168 | v7_crval: | 172 | v7_crval: |
169 | crval clear=0x0120c302, mmuset=0x10c03c7d, ucset=0x00c01c7c | 173 | crval clear=0x2120c302, mmuset=0x10c03c7d, ucset=0x00c01c7c |
170 | 174 | ||
171 | .previous | 175 | .previous |
diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S index 8de0f1dd1549..7b56386f9496 100644 --- a/arch/arm/mm/proc-v7-3level.S +++ b/arch/arm/mm/proc-v7-3level.S | |||
@@ -65,8 +65,11 @@ ENDPROC(cpu_v7_switch_mm) | |||
65 | */ | 65 | */ |
66 | ENTRY(cpu_v7_set_pte_ext) | 66 | ENTRY(cpu_v7_set_pte_ext) |
67 | #ifdef CONFIG_MMU | 67 | #ifdef CONFIG_MMU |
68 | tst r2, #L_PTE_PRESENT | 68 | tst r2, #L_PTE_VALID |
69 | beq 1f | 69 | beq 1f |
70 | tst r3, #1 << (57 - 32) @ L_PTE_NONE | ||
71 | bicne r2, #L_PTE_VALID | ||
72 | bne 1f | ||
70 | tst r3, #1 << (55 - 32) @ L_PTE_DIRTY | 73 | tst r3, #1 << (55 - 32) @ L_PTE_DIRTY |
71 | orreq r2, #L_PTE_RDONLY | 74 | orreq r2, #L_PTE_RDONLY |
72 | 1: strd r2, r3, [r0] | 75 | 1: strd r2, r3, [r0] |