aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/mm/tlb-r4k.c
diff options
context:
space:
mode:
authorMarkos Chandras <markos.chandras@imgtec.com>2014-07-14 07:47:09 -0400
committerRalf Baechle <ralf@linux-mips.org>2014-08-01 18:06:39 -0400
commitf1014d1b79d0fde02befadb0ca9e4da08ef8d453 (patch)
treede74b6272d4d5a4ec3676466de8a1e3b37eebeb2 /arch/mips/mm/tlb-r4k.c
parent3d528b326d7da8e28ec62c2ff1a92e85d93af098 (diff)
MIPS: mm: Use the Hardware Page Table Walker if the core supports it
The Hardware Page Table Walker aims to speed up TLB refill exceptions by handling them in the hardware level instead of having a software TLB refill handler. However, a TLB refill exception can still be thrown in certain cases such as, synchronus exceptions, or address translation or memory errors during the HTW operation. As a result of which, HTW must not be considered a complete replacement for the TLB refill software handler, but rather a fast-path for it. For HTW to work, the PWBase register must contain the task's page global directory address so the HTW will kick in on TLB refill exceptions. Due to HTW being a separate engine embedded deep in the CPU pipeline, we need to restart the HTW everytime a PTE changes to avoid HTW fetching a old entry from the page tables. It's also necessary to restart the HTW on context switches to prevent it from fetching a page from the previous process. Finally, since HTW is using the entryhi register to write the translations to the TLB, it's necessary to stop the HTW whenever the entryhi changes (eg for tlb probe perations) and enable it back afterwards. == Performance == The following trivial test was used to measure the performance of the HTW. Using the same root filesystem, the following command was used to measure the number of tlb refill handler executions with and without (using 'nohtw' kernel parameter) HTW support. The kernel was modified to use a scratch register as a counter for the TLB refill exceptions. find /usr -type f -exec ls -lh {} \; HTW Enabled: TLB refill exceptions: 12306 HTW Disabled: TLB refill exceptions: 17805 Signed-off-by: Markos Chandras <markos.chandras@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Markos Chandras <markos.chandras@imgtec.com> Patchwork: https://patchwork.linux-mips.org/patch/7336/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/mm/tlb-r4k.c')
-rw-r--r--arch/mips/mm/tlb-r4k.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c
index 92c9efdb1e86..fa6ebd4bc9e9 100644
--- a/arch/mips/mm/tlb-r4k.c
+++ b/arch/mips/mm/tlb-r4k.c
@@ -57,6 +57,7 @@ void local_flush_tlb_all(void)
57 local_irq_save(flags); 57 local_irq_save(flags);
58 /* Save old context and create impossible VPN2 value */ 58 /* Save old context and create impossible VPN2 value */
59 old_ctx = read_c0_entryhi(); 59 old_ctx = read_c0_entryhi();
60 htw_stop();
60 write_c0_entrylo0(0); 61 write_c0_entrylo0(0);
61 write_c0_entrylo1(0); 62 write_c0_entrylo1(0);
62 63
@@ -90,6 +91,7 @@ void local_flush_tlb_all(void)
90 } 91 }
91 tlbw_use_hazard(); 92 tlbw_use_hazard();
92 write_c0_entryhi(old_ctx); 93 write_c0_entryhi(old_ctx);
94 htw_start();
93 flush_itlb(); 95 flush_itlb();
94 local_irq_restore(flags); 96 local_irq_restore(flags);
95} 97}
@@ -131,6 +133,7 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
131 int oldpid = read_c0_entryhi(); 133 int oldpid = read_c0_entryhi();
132 int newpid = cpu_asid(cpu, mm); 134 int newpid = cpu_asid(cpu, mm);
133 135
136 htw_stop();
134 while (start < end) { 137 while (start < end) {
135 int idx; 138 int idx;
136 139
@@ -151,6 +154,7 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
151 } 154 }
152 tlbw_use_hazard(); 155 tlbw_use_hazard();
153 write_c0_entryhi(oldpid); 156 write_c0_entryhi(oldpid);
157 htw_start();
154 } else { 158 } else {
155 drop_mmu_context(mm, cpu); 159 drop_mmu_context(mm, cpu);
156 } 160 }
@@ -174,6 +178,7 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
174 start &= (PAGE_MASK << 1); 178 start &= (PAGE_MASK << 1);
175 end += ((PAGE_SIZE << 1) - 1); 179 end += ((PAGE_SIZE << 1) - 1);
176 end &= (PAGE_MASK << 1); 180 end &= (PAGE_MASK << 1);
181 htw_stop();
177 182
178 while (start < end) { 183 while (start < end) {
179 int idx; 184 int idx;
@@ -195,6 +200,7 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
195 } 200 }
196 tlbw_use_hazard(); 201 tlbw_use_hazard();
197 write_c0_entryhi(pid); 202 write_c0_entryhi(pid);
203 htw_start();
198 } else { 204 } else {
199 local_flush_tlb_all(); 205 local_flush_tlb_all();
200 } 206 }
@@ -214,6 +220,7 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
214 page &= (PAGE_MASK << 1); 220 page &= (PAGE_MASK << 1);
215 local_irq_save(flags); 221 local_irq_save(flags);
216 oldpid = read_c0_entryhi(); 222 oldpid = read_c0_entryhi();
223 htw_stop();
217 write_c0_entryhi(page | newpid); 224 write_c0_entryhi(page | newpid);
218 mtc0_tlbw_hazard(); 225 mtc0_tlbw_hazard();
219 tlb_probe(); 226 tlb_probe();
@@ -231,6 +238,7 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
231 238
232 finish: 239 finish:
233 write_c0_entryhi(oldpid); 240 write_c0_entryhi(oldpid);
241 htw_start();
234 flush_itlb_vm(vma); 242 flush_itlb_vm(vma);
235 local_irq_restore(flags); 243 local_irq_restore(flags);
236 } 244 }
@@ -247,6 +255,7 @@ void local_flush_tlb_one(unsigned long page)
247 255
248 local_irq_save(flags); 256 local_irq_save(flags);
249 oldpid = read_c0_entryhi(); 257 oldpid = read_c0_entryhi();
258 htw_stop();
250 page &= (PAGE_MASK << 1); 259 page &= (PAGE_MASK << 1);
251 write_c0_entryhi(page); 260 write_c0_entryhi(page);
252 mtc0_tlbw_hazard(); 261 mtc0_tlbw_hazard();
@@ -263,6 +272,7 @@ void local_flush_tlb_one(unsigned long page)
263 tlbw_use_hazard(); 272 tlbw_use_hazard();
264 } 273 }
265 write_c0_entryhi(oldpid); 274 write_c0_entryhi(oldpid);
275 htw_start();
266 flush_itlb(); 276 flush_itlb();
267 local_irq_restore(flags); 277 local_irq_restore(flags);
268} 278}
@@ -351,6 +361,7 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
351 local_irq_save(flags); 361 local_irq_save(flags);
352 /* Save old context and create impossible VPN2 value */ 362 /* Save old context and create impossible VPN2 value */
353 old_ctx = read_c0_entryhi(); 363 old_ctx = read_c0_entryhi();
364 htw_stop();
354 old_pagemask = read_c0_pagemask(); 365 old_pagemask = read_c0_pagemask();
355 wired = read_c0_wired(); 366 wired = read_c0_wired();
356 write_c0_wired(wired + 1); 367 write_c0_wired(wired + 1);
@@ -366,6 +377,7 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
366 377
367 write_c0_entryhi(old_ctx); 378 write_c0_entryhi(old_ctx);
368 tlbw_use_hazard(); /* What is the hazard here? */ 379 tlbw_use_hazard(); /* What is the hazard here? */
380 htw_start();
369 write_c0_pagemask(old_pagemask); 381 write_c0_pagemask(old_pagemask);
370 local_flush_tlb_all(); 382 local_flush_tlb_all();
371 local_irq_restore(flags); 383 local_irq_restore(flags);