aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/include/asm/cputhreads.h16
-rw-r--r--arch/powerpc/mm/mmu_context_nohash.c93
-rw-r--r--arch/powerpc/mm/tlb_nohash.c10
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
79static 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);
71static unsigned int steal_context_smp(unsigned int id) 81static 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 */
267int init_new_context(struct task_struct *t, struct mm_struct *mm) 286int 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
88static DEFINE_SPINLOCK(tlbivax_lock); 88static DEFINE_SPINLOCK(tlbivax_lock);
89 89
90static 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
90struct tlb_flush_param { 96struct 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);