diff options
-rw-r--r-- | arch/powerpc/include/asm/cputhreads.h | 16 | ||||
-rw-r--r-- | arch/powerpc/mm/mmu_context_nohash.c | 93 | ||||
-rw-r--r-- | arch/powerpc/mm/tlb_nohash.c | 10 |
3 files changed, 86 insertions, 33 deletions
diff --git a/arch/powerpc/include/asm/cputhreads.h b/arch/powerpc/include/asm/cputhreads.h index fb11b0c459b8..a8e18447c62b 100644 --- a/arch/powerpc/include/asm/cputhreads.h +++ b/arch/powerpc/include/asm/cputhreads.h | |||
@@ -5,6 +5,15 @@ | |||
5 | 5 | ||
6 | /* | 6 | /* |
7 | * Mapping of threads to cores | 7 | * Mapping of threads to cores |
8 | * | ||
9 | * Note: This implementation is limited to a power of 2 number of | ||
10 | * threads per core and the same number for each core in the system | ||
11 | * (though it would work if some processors had less threads as long | ||
12 | * as the CPU numbers are still allocated, just not brought offline). | ||
13 | * | ||
14 | * However, the API allows for a different implementation in the future | ||
15 | * if needed, as long as you only use the functions and not the variables | ||
16 | * directly. | ||
8 | */ | 17 | */ |
9 | 18 | ||
10 | #ifdef CONFIG_SMP | 19 | #ifdef CONFIG_SMP |
@@ -67,5 +76,12 @@ static inline int cpu_first_thread_in_core(int cpu) | |||
67 | return cpu & ~(threads_per_core - 1); | 76 | return cpu & ~(threads_per_core - 1); |
68 | } | 77 | } |
69 | 78 | ||
79 | static inline int cpu_last_thread_in_core(int cpu) | ||
80 | { | ||
81 | return cpu | (threads_per_core - 1); | ||
82 | } | ||
83 | |||
84 | |||
85 | |||
70 | #endif /* _ASM_POWERPC_CPUTHREADS_H */ | 86 | #endif /* _ASM_POWERPC_CPUTHREADS_H */ |
71 | 87 | ||
diff --git a/arch/powerpc/mm/mmu_context_nohash.c b/arch/powerpc/mm/mmu_context_nohash.c index b1a727def15b..834436d6d6b8 100644 --- a/arch/powerpc/mm/mmu_context_nohash.c +++ b/arch/powerpc/mm/mmu_context_nohash.c | |||
@@ -25,10 +25,20 @@ | |||
25 | * also clear mm->cpu_vm_mask bits when processes are migrated | 25 | * also clear mm->cpu_vm_mask bits when processes are migrated |
26 | */ | 26 | */ |
27 | 27 | ||
28 | #undef DEBUG | 28 | #define DEBUG_MAP_CONSISTENCY |
29 | #define DEBUG_STEAL_ONLY | 29 | #define DEBUG_CLAMP_LAST_CONTEXT 31 |
30 | #undef DEBUG_MAP_CONSISTENCY | 30 | //#define DEBUG_HARDER |
31 | /*#define DEBUG_CLAMP_LAST_CONTEXT 15 */ | 31 | |
32 | /* We don't use DEBUG because it tends to be compiled in always nowadays | ||
33 | * and this would generate way too much output | ||
34 | */ | ||
35 | #ifdef DEBUG_HARDER | ||
36 | #define pr_hard(args...) printk(KERN_DEBUG args) | ||
37 | #define pr_hardcont(args...) printk(KERN_CONT args) | ||
38 | #else | ||
39 | #define pr_hard(args...) do { } while(0) | ||
40 | #define pr_hardcont(args...) do { } while(0) | ||
41 | #endif | ||
32 | 42 | ||
33 | #include <linux/kernel.h> | 43 | #include <linux/kernel.h> |
34 | #include <linux/mm.h> | 44 | #include <linux/mm.h> |
@@ -71,7 +81,7 @@ static DEFINE_SPINLOCK(context_lock); | |||
71 | static unsigned int steal_context_smp(unsigned int id) | 81 | static unsigned int steal_context_smp(unsigned int id) |
72 | { | 82 | { |
73 | struct mm_struct *mm; | 83 | struct mm_struct *mm; |
74 | unsigned int cpu, max; | 84 | unsigned int cpu, max, i; |
75 | 85 | ||
76 | max = last_context - first_context; | 86 | max = last_context - first_context; |
77 | 87 | ||
@@ -89,15 +99,22 @@ static unsigned int steal_context_smp(unsigned int id) | |||
89 | id = first_context; | 99 | id = first_context; |
90 | continue; | 100 | continue; |
91 | } | 101 | } |
92 | pr_devel("[%d] steal context %d from mm @%p\n", | 102 | pr_hardcont(" | steal %d from 0x%p", id, mm); |
93 | smp_processor_id(), id, mm); | ||
94 | 103 | ||
95 | /* Mark this mm has having no context anymore */ | 104 | /* Mark this mm has having no context anymore */ |
96 | mm->context.id = MMU_NO_CONTEXT; | 105 | mm->context.id = MMU_NO_CONTEXT; |
97 | 106 | ||
98 | /* Mark it stale on all CPUs that used this mm */ | 107 | /* Mark it stale on all CPUs that used this mm. For threaded |
99 | for_each_cpu(cpu, mm_cpumask(mm)) | 108 | * implementations, we set it on all threads on each core |
100 | __set_bit(id, stale_map[cpu]); | 109 | * represented in the mask. A future implementation will use |
110 | * a core map instead but this will do for now. | ||
111 | */ | ||
112 | for_each_cpu(cpu, mm_cpumask(mm)) { | ||
113 | for (i = cpu_first_thread_in_core(cpu); | ||
114 | i <= cpu_last_thread_in_core(cpu); i++) | ||
115 | __set_bit(id, stale_map[i]); | ||
116 | cpu = i - 1; | ||
117 | } | ||
101 | return id; | 118 | return id; |
102 | } | 119 | } |
103 | 120 | ||
@@ -126,7 +143,7 @@ static unsigned int steal_context_up(unsigned int id) | |||
126 | /* Pick up the victim mm */ | 143 | /* Pick up the victim mm */ |
127 | mm = context_mm[id]; | 144 | mm = context_mm[id]; |
128 | 145 | ||
129 | pr_devel("[%d] steal context %d from mm @%p\n", cpu, id, mm); | 146 | pr_hardcont(" | steal %d from 0x%p", id, mm); |
130 | 147 | ||
131 | /* Flush the TLB for that context */ | 148 | /* Flush the TLB for that context */ |
132 | local_flush_tlb_mm(mm); | 149 | local_flush_tlb_mm(mm); |
@@ -179,19 +196,14 @@ void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next) | |||
179 | /* No lockless fast path .. yet */ | 196 | /* No lockless fast path .. yet */ |
180 | spin_lock(&context_lock); | 197 | spin_lock(&context_lock); |
181 | 198 | ||
182 | #ifndef DEBUG_STEAL_ONLY | 199 | pr_hard("[%d] activating context for mm @%p, active=%d, id=%d", |
183 | pr_devel("[%d] activating context for mm @%p, active=%d, id=%d\n", | 200 | cpu, next, next->context.active, next->context.id); |
184 | cpu, next, next->context.active, next->context.id); | ||
185 | #endif | ||
186 | 201 | ||
187 | #ifdef CONFIG_SMP | 202 | #ifdef CONFIG_SMP |
188 | /* Mark us active and the previous one not anymore */ | 203 | /* Mark us active and the previous one not anymore */ |
189 | next->context.active++; | 204 | next->context.active++; |
190 | if (prev) { | 205 | if (prev) { |
191 | #ifndef DEBUG_STEAL_ONLY | 206 | pr_hardcont(" (old=0x%p a=%d)", prev, prev->context.active); |
192 | pr_devel(" old context %p active was: %d\n", | ||
193 | prev, prev->context.active); | ||
194 | #endif | ||
195 | WARN_ON(prev->context.active < 1); | 207 | WARN_ON(prev->context.active < 1); |
196 | prev->context.active--; | 208 | prev->context.active--; |
197 | } | 209 | } |
@@ -201,8 +213,14 @@ void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next) | |||
201 | 213 | ||
202 | /* If we already have a valid assigned context, skip all that */ | 214 | /* If we already have a valid assigned context, skip all that */ |
203 | id = next->context.id; | 215 | id = next->context.id; |
204 | if (likely(id != MMU_NO_CONTEXT)) | 216 | if (likely(id != MMU_NO_CONTEXT)) { |
217 | #ifdef DEBUG_MAP_CONSISTENCY | ||
218 | if (context_mm[id] != next) | ||
219 | pr_err("MMU: mm 0x%p has id %d but context_mm[%d] says 0x%p\n", | ||
220 | next, id, id, context_mm[id]); | ||
221 | #endif | ||
205 | goto ctxt_ok; | 222 | goto ctxt_ok; |
223 | } | ||
206 | 224 | ||
207 | /* We really don't have a context, let's try to acquire one */ | 225 | /* We really don't have a context, let's try to acquire one */ |
208 | id = next_context; | 226 | id = next_context; |
@@ -235,11 +253,7 @@ void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next) | |||
235 | next_context = id + 1; | 253 | next_context = id + 1; |
236 | context_mm[id] = next; | 254 | context_mm[id] = next; |
237 | next->context.id = id; | 255 | next->context.id = id; |
238 | 256 | pr_hardcont(" | new id=%d,nrf=%d", id, nr_free_contexts); | |
239 | #ifndef DEBUG_STEAL_ONLY | ||
240 | pr_devel("[%d] picked up new id %d, nrf is now %d\n", | ||
241 | cpu, id, nr_free_contexts); | ||
242 | #endif | ||
243 | 257 | ||
244 | context_check_map(); | 258 | context_check_map(); |
245 | ctxt_ok: | 259 | ctxt_ok: |
@@ -248,15 +262,20 @@ void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next) | |||
248 | * local TLB for it and unmark it before we use it | 262 | * local TLB for it and unmark it before we use it |
249 | */ | 263 | */ |
250 | if (test_bit(id, stale_map[cpu])) { | 264 | if (test_bit(id, stale_map[cpu])) { |
251 | pr_devel("[%d] flushing stale context %d for mm @%p !\n", | 265 | pr_hardcont(" | stale flush %d [%d..%d]", |
252 | cpu, id, next); | 266 | id, cpu_first_thread_in_core(cpu), |
267 | cpu_last_thread_in_core(cpu)); | ||
268 | |||
253 | local_flush_tlb_mm(next); | 269 | local_flush_tlb_mm(next); |
254 | 270 | ||
255 | /* XXX This clear should ultimately be part of local_flush_tlb_mm */ | 271 | /* XXX This clear should ultimately be part of local_flush_tlb_mm */ |
256 | __clear_bit(id, stale_map[cpu]); | 272 | for (cpu = cpu_first_thread_in_core(cpu); |
273 | cpu <= cpu_last_thread_in_core(cpu); cpu++) | ||
274 | __clear_bit(id, stale_map[cpu]); | ||
257 | } | 275 | } |
258 | 276 | ||
259 | /* Flick the MMU and release lock */ | 277 | /* Flick the MMU and release lock */ |
278 | pr_hardcont(" -> %d\n", id); | ||
260 | set_context(id, next->pgd); | 279 | set_context(id, next->pgd); |
261 | spin_unlock(&context_lock); | 280 | spin_unlock(&context_lock); |
262 | } | 281 | } |
@@ -266,6 +285,8 @@ void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next) | |||
266 | */ | 285 | */ |
267 | int init_new_context(struct task_struct *t, struct mm_struct *mm) | 286 | int init_new_context(struct task_struct *t, struct mm_struct *mm) |
268 | { | 287 | { |
288 | pr_hard("initing context for mm @%p\n", mm); | ||
289 | |||
269 | mm->context.id = MMU_NO_CONTEXT; | 290 | mm->context.id = MMU_NO_CONTEXT; |
270 | mm->context.active = 0; | 291 | mm->context.active = 0; |
271 | 292 | ||
@@ -305,7 +326,9 @@ static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self, | |||
305 | unsigned long action, void *hcpu) | 326 | unsigned long action, void *hcpu) |
306 | { | 327 | { |
307 | unsigned int cpu = (unsigned int)(long)hcpu; | 328 | unsigned int cpu = (unsigned int)(long)hcpu; |
308 | 329 | #ifdef CONFIG_HOTPLUG_CPU | |
330 | struct task_struct *p; | ||
331 | #endif | ||
309 | /* We don't touch CPU 0 map, it's allocated at aboot and kept | 332 | /* We don't touch CPU 0 map, it's allocated at aboot and kept |
310 | * around forever | 333 | * around forever |
311 | */ | 334 | */ |
@@ -324,8 +347,16 @@ static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self, | |||
324 | pr_devel("MMU: Freeing stale context map for CPU %d\n", cpu); | 347 | pr_devel("MMU: Freeing stale context map for CPU %d\n", cpu); |
325 | kfree(stale_map[cpu]); | 348 | kfree(stale_map[cpu]); |
326 | stale_map[cpu] = NULL; | 349 | stale_map[cpu] = NULL; |
327 | break; | 350 | |
328 | #endif | 351 | /* We also clear the cpu_vm_mask bits of CPUs going away */ |
352 | read_lock(&tasklist_lock); | ||
353 | for_each_process(p) { | ||
354 | if (p->mm) | ||
355 | cpu_mask_clear_cpu(cpu, mm_cpumask(p->mm)); | ||
356 | } | ||
357 | read_unlock(&tasklist_lock); | ||
358 | break; | ||
359 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
329 | } | 360 | } |
330 | return NOTIFY_OK; | 361 | return NOTIFY_OK; |
331 | } | 362 | } |
diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c index ad2eb4d34dd4..d908e75cc3b5 100644 --- a/arch/powerpc/mm/tlb_nohash.c +++ b/arch/powerpc/mm/tlb_nohash.c | |||
@@ -87,6 +87,12 @@ EXPORT_SYMBOL(local_flush_tlb_page); | |||
87 | 87 | ||
88 | static DEFINE_SPINLOCK(tlbivax_lock); | 88 | static DEFINE_SPINLOCK(tlbivax_lock); |
89 | 89 | ||
90 | static int mm_is_core_local(struct mm_struct *mm) | ||
91 | { | ||
92 | return cpumask_subset(mm_cpumask(mm), | ||
93 | topology_thread_cpumask(smp_processor_id())); | ||
94 | } | ||
95 | |||
90 | struct tlb_flush_param { | 96 | struct tlb_flush_param { |
91 | unsigned long addr; | 97 | unsigned long addr; |
92 | unsigned int pid; | 98 | unsigned int pid; |
@@ -131,7 +137,7 @@ void flush_tlb_mm(struct mm_struct *mm) | |||
131 | pid = mm->context.id; | 137 | pid = mm->context.id; |
132 | if (unlikely(pid == MMU_NO_CONTEXT)) | 138 | if (unlikely(pid == MMU_NO_CONTEXT)) |
133 | goto no_context; | 139 | goto no_context; |
134 | if (!cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) { | 140 | if (!mm_is_core_local(mm)) { |
135 | struct tlb_flush_param p = { .pid = pid }; | 141 | struct tlb_flush_param p = { .pid = pid }; |
136 | /* Ignores smp_processor_id() even if set. */ | 142 | /* Ignores smp_processor_id() even if set. */ |
137 | smp_call_function_many(mm_cpumask(mm), | 143 | smp_call_function_many(mm_cpumask(mm), |
@@ -153,7 +159,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) | |||
153 | if (unlikely(pid == MMU_NO_CONTEXT)) | 159 | if (unlikely(pid == MMU_NO_CONTEXT)) |
154 | goto bail; | 160 | goto bail; |
155 | cpu_mask = mm_cpumask(vma->vm_mm); | 161 | cpu_mask = mm_cpumask(vma->vm_mm); |
156 | if (!cpumask_equal(cpu_mask, cpumask_of(smp_processor_id()))) { | 162 | if (!mm_is_core_local(mm)) { |
157 | /* If broadcast tlbivax is supported, use it */ | 163 | /* If broadcast tlbivax is supported, use it */ |
158 | if (mmu_has_feature(MMU_FTR_USE_TLBIVAX_BCAST)) { | 164 | if (mmu_has_feature(MMU_FTR_USE_TLBIVAX_BCAST)) { |
159 | int lock = mmu_has_feature(MMU_FTR_LOCK_BCAST_INVAL); | 165 | int lock = mmu_has_feature(MMU_FTR_LOCK_BCAST_INVAL); |