TLB preloading on 8xx
Marcelo Tosatti
marcelo.tosatti at cyclades.com
Wed Dec 21 04:37:30 EST 2005
Hi,
Been playing with TLB preloading on 8xx for the past weeks, and I must
say that results are frustrating.
Most of the TLB setup work involves writing to special purpose
registers, using "mtspr", which is a serializing instruction (it blocks
all execution units).
Sum up the costs of disabling interrupts and disabling translation, and you
end up with a slow dog. Damn, the TLB exceptions are indeed efficient.
The test used to measure pagefault latency is LMbench's "lat_pagefault".
vanilla:
[root at CAS /]# ./lat_pagefault -N10 out.prof
Pagefaults on out.prof: 36.3728 microseconds
d-tlb-preload:
[root at CAS /]# ./lat_pagefault -N10 out.prof
Pagefaults on out.prof: 43.7793 microseconds
diff -Nur --exclude-from=linux-2.6-git-dec01/Documentation/dontdiff linux-2.6-git-dec01.orig/arch/ppc/kernel/head_8xx.S linux-2.6-git-dec01/arch/ppc/kernel/head_8xx.S
--- linux-2.6-git-dec01.orig/arch/ppc/kernel/head_8xx.S 2005-12-05 09:47:27.000000000 -0600
+++ linux-2.6-git-dec01/arch/ppc/kernel/head_8xx.S 2005-12-15 12:37:07.449818656 -0600
@@ -804,7 +828,156 @@
SYNC
blr
+
+_GLOBAL(__tlb_data_load)
+ rlwinm r8, r4, 0, 0, 19 /* extract page address */
+ ori r8, r8, MD_EVALID /* set valid bit */
+ slw r3, r3, 28
+ rlwimi r8, r3, 0, 28, 31 /* load ASID from r3 */
+#ifdef CONFIG_8xx_CPU6
+ li r9, 0x3780;
+ stw r9, 4(r7);
+ lwz r9, 4(r7);
+#endif
+ mtspr SPRN_MD_EPN, r8
+
+ mfspr r10, SPRN_M_TWB /* Get level 1 table entry address */
+ lwz r11, 0(r10) /* Get the level 1 entry */
+ ori r11,r11,1 /* Set valid bit */
+
+ /* Insert the Guarded flag into the TWC from the Linux PTE.
+ * It is bit 27 of both the Linux PTE and the TWC (at least
+ * I got that right :-). It will be better when we can put
+ * this into the Linux pgd/pmd and load it in the operation
+ * above.
+ */
+ mr r12, r5
+ rlwimi r11, r12, 0, 27, 27
+
+ /*
+ * Some fields of MD_TWC are cleared by the CPU on a DTLB miss.
+ * Must do it manually for TLB preload.
+ * clear 23-26 (access protection group)
+ * clear 28-29 (page size) and 30 (write-through)
+ */
+ li r12, 0
+ rlwimi r11, r12, 0, 23, 26
+ rlwimi r11, r12, 0, 28, 30
+#ifdef CONFIG_8xx_CPU6
+ li r9, 0x3b80;
+ stw r9, 4(r7);
+ lwz r9, 4(r7);
+#endif
+ mtspr SPRN_MD_TWC, r11 /* Set segment attributes */
+
+ mr r8, r5
+ mr r11, r8
+ rlwinm r8, r8, 0, 0, 20
+ ori r8, r8, 1 /* set valid bit */
+ /* Update 'changed', among others.
+ */
+ andi. r11, r11, _PAGE_RW
+ li r11, 0x00f0
+ beq 1f
+ ori r8, r8, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE
+// stw r8, 0(r5) /* and update pte in table */
+ ori r11, r11, _PAGE_HWWRITE
+ /* The Linux PTE won't go exactly into the MMU TLB.
+ * Software indicator bits 21, 22 and 28 must be clear.
+ * Software indicator bits 24, 25, 26, and 27 must be
+ * set. All other Linux PTE bits control the behavior
+ * of the MMU.
+ */
+1:
+ rlwimi r8, r11, 0, 23, 28 /* Set 24-27, clear 28 */
+ /* 23 is set if page is _PAGE_RW */
+#ifdef CONFIG_8xx_CPU6
+ li r9, 0x3d80;
+ stw r9, 4(r7);
+ lwz r9, 4(r7);
+#endif
+ mtspr SPRN_MD_RPN, r8 /* Update TLB entry */
+
+ mfmsr r11
+ lwz r6, 0(r7) /* restore Link Register */
+ mtlr r6
+ li r6, 0x7fff
+ rlwimi r11, r6, 0, 27, 27 /* set DR */
+ mtmsr r11
+ tovirt(r7, r7)
+ blr
+
+/*
+ * Load a D-TLB entry.
+ * r3: context number
+ * r4: effective address
+ * r5: PTE pointer
+ * r6: PMD (level-1 entry)
+ * r7: temp location
+ */
+_GLOBAL(tlb_data_load)
+ lwz r5, 0(r5)
+ mflr r6
+ stw r6, 0(r7) /* save Link Register */
+ mfmsr r11
+ li r6, 0
+ rlwimi r11, r6, 0, 27, 27 /* clear DR (data translat.)*/
+ mtmsr r11
+ lis r6, __tlb_data_load at h
+ ori r6, r6, __tlb_data_load at l
+ tophys(r7, r7)
+ mtlr r6
+ blr
+
+/*
+ * Load a I-TLB entry.
+ * r3: context number
+ * r4: effective address
+ * r5: PTE pointer
+ * r6: PMD (level-1 entry)
+ * r7: temp location
+ */
+_GLOBAL(tlb_instr_load)
+ rlwinm r8, r4, 0, 0, 19 /* extract page address */
+ ori r8, r8, MI_EVALID /* set valid bit */
+ slw r3, r3, 28
+ rlwimi r8, r3, 0, 28, 31 /* load ASID from r3 */
#ifdef CONFIG_8xx_CPU6
+ li r9, 0x2780;
+ stw r9, 0(r7);
+ lwz r9, 0(r7);
+#endif
+
+ mfspr r10, SPRN_M_TWB /* Get level 1 table entry address */
+ tovirt(r10, r10)
+ lwz r11, 0(r10) /* Get the level 1 entry */
+ ori r11,r11,1 /* Set valid bit */
+#ifdef CONFIG_8xx_CPU6
+ li r9, 0x2b80;
+ stw r9, 0(r7);
+ lwz r9, 0(r7);
+#endif
+ mtspr SPRN_MI_TWC, r11 /* Set segment attributes */
+
+ lwz r8, 0(r5)
+ rlwinm r8, r8, 0, 0, 19
+ ori r8, r8, 1 /* set valid bit */
+ /* The Linux PTE won't go exactly into the MMU TLB.
+ * Software indicator bits 21, 22 and 28 must be clear.
+ * Software indicator bits 24, 25, 26, and 27 must be
+ * set. All other Linux PTE bits control the behavior
+ * of the MMU.
+ */
+ li r11, 0x00f0
+ rlwimi r8, r11, 0, 24, 28 /* Set 24-27, clear 28 */
+#ifdef CONFIG_8xx_CPU6
+ li r9, 0x2d80;
+ stw r9, 0(r7);
+ lwz r9, 0(r7);
+#endif
+ mtspr SPRN_MI_RPN, r8 /* Update TLB entry */
+ blr
+
/* It's here because it is unique to the 8xx.
* It is important we get called with interrupts disabled. I used to
* do that, but it appears that all code that calls this already had
@@ -820,7 +993,6 @@
mtspr 22, r3 /* Update Decrementer */
SYNC
blr
-#endif
/*
* We put a few things here that have to be page-aligned.
diff -Nur --exclude-from=linux-2.6-git-dec01/Documentation/dontdiff linux-2.6-git-dec01.orig/arch/ppc/mm/init.c linux-2.6-git-dec01/arch/ppc/mm/init.c
--- linux-2.6-git-dec01.orig/arch/ppc/mm/init.c 2005-12-05 09:47:28.000000000 -0600
+++ linux-2.6-git-dec01/arch/ppc/mm/init.c 2005-12-15 13:16:42.787712408 -0600
@@ -583,6 +583,54 @@
kunmap(page);
}
+extern void tlb_data_load(unsigned long id, unsigned long address, pte_t *pte,
+ unsigned long pmdval, unsigned long *tmpval);
+
+extern void tlb_instr_load(unsigned long id, unsigned long address, pte_t *pte,
+ unsigned long pmdval, unsigned long *tmpval);
+
+void tlb_preload(struct vm_area_struct *vma, unsigned long address,
+ pte_t pte)
+{
+ struct mm_struct *mm;
+ pmd_t *pmd;
+ pte_t *ptep;
+ int mapping_executable = 0;
+ unsigned long flags, tmpval;
+ unsigned long tmp[4];
+
+ if ((vma->vm_flags & VM_EXEC) == VM_EXEC)
+ mapping_executable = 1;
+
+ local_irq_save(flags);
+
+ mm = vma->vm_mm;
+ pmd = pmd_offset(pgd_offset(mm, address), address);
+ if (!pmd_none(*pmd)) {
+ if (mfspr(SPRN_M_CASID) != (mm->context)) {
+ printk(KERN_ERR "CASID:%lx mm->context:%lx\n",
+ mfspr(SPRN_M_CASID), (mm->context));
+ BUG();
+ }
+ ptep = pte_offset_map(pmd, address);
+ if (!pte_present(pte) || !ptep)
+ goto out;
+ if (!mapping_executable)
+ tlb_data_load(mm->context, address, ptep,
+ pmd_val(*pmd), &tmp);
+#ifdef NOTYET
+ else
+ tlb_instr_load(mm->context, address, ptep,
+ pmd_val(*pmd), &tmp);
+#endif
+out:
+ pte_unmap(ptep);
+ }
+ local_irq_restore(flags);
+}
+
+extern void tlbie_efficient(unsigned long address, struct vm_area_struct *vma);
+
/*
* This is called at the end of handling a user page fault, when the
* fault has been handled by updating a PTE in the linux page tables.
@@ -614,6 +662,7 @@
flush_dcache_icache_page(page);
set_bit(PG_arch_1, &page->flags);
}
+ tlb_preload(vma, address, pte);
}
#ifdef CONFIG_PPC_STD_MMU
More information about the Linuxppc-embedded
mailing list