From: Jinyu Tang Date: Sun, 17 May 2026 15:34:25 +0000 (+0800) Subject: KVM: riscv: Add a G-stage PTE cmpxchg helper X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=7dd416fdd3fba2095c3cea58e36f8ee5d86dadc3;p=thirdparty%2Flinux.git KVM: riscv: Add a G-stage PTE cmpxchg helper Permission-only G-stage PTE updates can run in parallel once they are moved to the read side of mmu_lock. Plain set_pte() is not enough for that case because another CPU may update the same PTE first. x86 handles the same class of SPTE races with cmpxchg-based updates in its fast page fault and TDP MMU paths. Add a small RISC-V helper for atomic G-stage PTE updates. The helper reports contention to the caller and flushes the target range only when the PTE value actually changes. Signed-off-by: Jinyu Tang Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260517153427.94889-4-tjytimi@163.com Signed-off-by: Anup Patel --- diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h index f820c6783e16..21e2019df0cf 100644 --- a/arch/riscv/include/asm/kvm_gstage.h +++ b/arch/riscv/include/asm/kvm_gstage.h @@ -54,6 +54,10 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, struct kvm_mmu_memory_cache *pcache, const struct kvm_gstage_mapping *map); +bool kvm_riscv_gstage_try_update_pte(struct kvm_gstage *gstage, u32 level, + gpa_t addr, pte_t *ptep, + pte_t old_pte, pte_t new_pte); + int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage, struct kvm_mmu_memory_cache *pcache, gpa_t gpa, phys_addr_t hpa, unsigned long page_size, diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c index d4ce5e76989c..d878100aeb35 100644 --- a/arch/riscv/kvm/gstage.c +++ b/arch/riscv/kvm/gstage.c @@ -123,6 +123,20 @@ static void gstage_tlb_flush(struct kvm_gstage *gstage, u32 level, gpa_t addr) gstage->vmid); } +bool kvm_riscv_gstage_try_update_pte(struct kvm_gstage *gstage, u32 level, + gpa_t addr, pte_t *ptep, + pte_t old_pte, pte_t new_pte) +{ + if (cmpxchg(&ptep->pte, pte_val(old_pte), pte_val(new_pte)) != + pte_val(old_pte)) + return false; + + if (pte_val(old_pte) != pte_val(new_pte)) + gstage_tlb_flush(gstage, level, addr); + + return true; +} + int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage, struct kvm_mmu_memory_cache *pcache, const struct kvm_gstage_mapping *map)