From 8fa3df98336ca2872dc51a8859142e8892844fb6 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 24 Jul 2025 20:34:35 +0800 Subject: [PATCH] target/loongarch: Reduce TLB flush with helper_tlbwr With function helper_tlbwr(), specified LoongArch TLB entry will be updated. There are two PTE pages in one TLB entry called even/odd pages. Supposing even/odd page is normal/none state, when odd page is added, TLB entry is changed as normal/normal state and even page keeps unchanged. In this situation, it is not necessary to flush QEMU TLB since even page keep unchanged and odd page is newly changed. Here check whether PTE page is the same or not, TLB flush can be skipped if both are the same or newly added. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 25dbbd0d775..88dba9eb3ea 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -302,16 +302,35 @@ void helper_tlbrd(CPULoongArchState *env) void helper_tlbwr(CPULoongArchState *env) { int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); + LoongArchTLB *old, new = {}; + bool skip_inv = false; + uint8_t tlb_v0, tlb_v1; - invalidate_tlb(env, index); - + old = env->tlb + index; if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) { - env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc, - TLB_MISC, E, 0); + invalidate_tlb(env, index); + old->tlb_misc = FIELD_DP64(old->tlb_misc, TLB_MISC, E, 0); return; } - fill_tlb_entry(env, env->tlb + index); + fill_tlb_entry(env, &new); + /* Check whether ASID/VPPN is the same */ + if (old->tlb_misc == new.tlb_misc) { + /* Check whether both even/odd pages is the same or invalid */ + tlb_v0 = FIELD_EX64(old->tlb_entry0, TLBENTRY, V); + tlb_v1 = FIELD_EX64(old->tlb_entry1, TLBENTRY, V); + if ((!tlb_v0 || new.tlb_entry0 == old->tlb_entry0) && + (!tlb_v1 || new.tlb_entry1 == old->tlb_entry1)) { + skip_inv = true; + } + } + + /* flush tlb before updating the entry */ + if (!skip_inv) { + invalidate_tlb(env, index); + } + + *old = new; } void helper_tlbfill(CPULoongArchState *env) -- 2.47.3