kvm_set_pte is called to replace a target PTE with a desired one. We always do this without changing the desired one, but if dirty status set by hardware is coverred, let caller know it. Signed-off-by: Keqian Zhu <zhukeqian1@xxxxxxxxxx> --- arch/arm64/kvm/mmu.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 5ad87bce23c0..27407153121b 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -194,11 +194,45 @@ static void clear_stage2_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr put_page(virt_to_page(pmd)); } -static inline void kvm_set_pte(pte_t *ptep, pte_t new_pte) +#ifdef CONFIG_ARM64_HW_AFDBM +/** + * @ret: true if dirty status set by hardware is coverred. + */ +static bool kvm_set_pte(pte_t *ptep, pte_t new_pte) +{ + pteval_t old_pteval, new_pteval, pteval; + bool old_logging, new_no_write; + + old_logging = kvm_hw_dbm_enabled() && !pte_none(*ptep) && + kvm_s2pte_dbm(ptep); + new_no_write = pte_none(new_pte) || kvm_s2pte_readonly(&new_pte); + + if (!old_logging || !new_no_write) { + WRITE_ONCE(*ptep, new_pte); + dsb(ishst); + return false; + } + + new_pteval = pte_val(new_pte); + pteval = READ_ONCE(pte_val(*ptep)); + do { + old_pteval = pteval; + pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval, new_pteval); + } while (pteval != old_pteval); + + return !kvm_s2pte_readonly(&__pte(pteval)); +} +#else +/** + * @ret: true if dirty status set by hardware is coverred. + */ +static inline bool kvm_set_pte(pte_t *ptep, pte_t new_pte) { WRITE_ONCE(*ptep, new_pte); dsb(ishst); + return false; } +#endif /* CONFIG_ARM64_HW_AFDBM */ static inline void kvm_set_pmd(pmd_t *pmdp, pmd_t new_pmd) { -- 2.19.1 _______________________________________________ kvmarm mailing list kvmarm@xxxxxxxxxxxxxxxxxxxxx https://lists.cs.columbia.edu/mailman/listinfo/kvmarm