If a tlb miss triggered when EXL=1, tlb refill exception is treated as tlb invalid exception, so tlbp may fails. In this situation, CP0_Index register doesn't contain a valid value. This may not be a problem for VTLB since it is fully-associative. However, FTLB is set-associative so not every tlb entry is valid for a specific address. Thus, we should use tlbwr instead of tlbwi when tlbp fails. There is a similar case for huge page, so build_huge_tlb_write_entry() is also modified. If wmode != tlb_random, that means the caller is tlb invalid handler, we should select tlbr/tlbi depend on the tlbp result. Signed-off-by: Huacai Chen <chenhc@xxxxxxxxxx> --- arch/mips/mm/tlbex.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index d0975cd..da68ffb 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -173,7 +173,10 @@ enum label_id { label_large_segbits_fault, #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT label_tlb_huge_update, + label_tail_huge_miss, + label_tail_huge_done, #endif + label_tail_miss, }; UASM_L_LA(_second_part) @@ -192,7 +195,10 @@ UASM_L_LA(_r3000_write_probe_fail) UASM_L_LA(_large_segbits_fault) #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT UASM_L_LA(_tlb_huge_update) +UASM_L_LA(_tail_huge_miss) +UASM_L_LA(_tail_huge_done) #endif +UASM_L_LA(_tail_miss) static int hazard_instance; @@ -706,8 +712,24 @@ static void build_huge_tlb_write_entry(u32 **p, struct uasm_label **l, uasm_i_ori(p, tmp, tmp, PM_HUGE_MASK & 0xffff); uasm_i_mtc0(p, tmp, C0_PAGEMASK); - build_tlb_write_entry(p, l, r, wmode); + if (wmode == tlb_random) { /* Caller is TLB Refill Handler */ + build_tlb_write_entry(p, l, r, wmode); + build_restore_pagemask(p, r, tmp, label_leave, restore_scratch); + return; + } + + /* Caller is TLB Load/Store/Modify Handler */ + uasm_i_mfc0(p, tmp, C0_INDEX); + uasm_il_bltz(p, r, tmp, label_tail_huge_miss); + uasm_i_nop(p); + build_tlb_write_entry(p, l, r, tlb_indexed); + uasm_il_b(p, r, label_tail_huge_done); + uasm_i_nop(p); + + uasm_l_tail_huge_miss(l, *p); + build_tlb_write_entry(p, l, r, tlb_random); + uasm_l_tail_huge_done(l, *p); build_restore_pagemask(p, r, tmp, label_leave, restore_scratch); } @@ -2026,7 +2048,14 @@ build_r4000_tlbchange_handler_tail(u32 **p, struct uasm_label **l, uasm_i_ori(p, ptr, ptr, sizeof(pte_t)); uasm_i_xori(p, ptr, ptr, sizeof(pte_t)); build_update_entries(p, tmp, ptr); + uasm_i_mfc0(p, ptr, C0_INDEX); + uasm_il_bltz(p, r, ptr, label_tail_miss); + uasm_i_nop(p); build_tlb_write_entry(p, l, r, tlb_indexed); + uasm_il_b(p, r, label_leave); + uasm_i_nop(p); + uasm_l_tail_miss(l, *p); + build_tlb_write_entry(p, l, r, tlb_random); uasm_l_leave(l, *p); build_restore_work_registers(p); uasm_i_eret(p); /* return from trap */ -- 2.4.6