]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: s390: cmma: Fix dirty tracking when removing memslot
authorClaudio Imbrenda <imbrenda@linux.ibm.com>
Mon, 3 Aug 2026 12:40:35 +0000 (14:40 +0200)
committerClaudio Imbrenda <imbrenda@linux.ibm.com>
Mon, 3 Aug 2026 14:51:34 +0000 (16:51 +0200)
When a memslot is removed, all ptes that mapped the slot are cleared or
even deallocated. If this happens while the system is in migration
mode, and if cmma-dirty pages are removed, the cmma-dirty counter will
not reflect reality.

Fix by appropriately decrementing the cmma-dirty counter when removing
a memslot.

Opportunistically improve kvm_arch_commit_memory_region() to use
__free() for the struct kvm_s390_mmu_cache.

Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Message-ID: <20260803124040.126471-9-imbrenda@linux.ibm.com>

arch/s390/kvm/dat.c
arch/s390/kvm/kvm-s390.c

index 171b6195990891399b0cb0ae0307911277c88c5b..3f2d6e8902d76a60cdddbfa2218d2172c308ca03 100644 (file)
@@ -850,6 +850,7 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
        struct slot_priv *p = walk->priv;
        union crste dummy = { .val = p->token };
        union pte new_pte, pte = READ_ONCE(*ptep);
+       union pgste pgste;
 
        new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par);
 
@@ -857,7 +858,11 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
        if (pte.val == new_pte.val)
                return 0;
 
-       dat_ptep_xchg(ptep, new_pte, gfn, walk->asce, false);
+       pgste = pgste_get_lock(ptep);
+       pgste = __dat_ptep_xchg(ptep, pgste, new_pte, gfn, walk->asce, false);
+       pgste.cmma_d = 0;
+       pgste_set_unlock(ptep, pgste);
+
        return 0;
 }
 
index e5c5e9f61cb24b220d12a5a962f2d9cd5aae1de8..ba811f0673d1073abbf84e8ae5574c9292bc4f98 100644 (file)
@@ -5812,14 +5812,30 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
        return 0;
 }
 
+static long cmma_d_count_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
+{
+       union pgste pgste;
+
+       pgste = pgste_get_lock(ptep);
+       if (pgste.cmma_d) {
+               pgste.cmma_d = 0;
+               atomic64_dec(walk->priv);
+       }
+       pgste_set_unlock(ptep, pgste);
+       return 0;
+}
+
 void kvm_arch_commit_memory_region(struct kvm *kvm,
                                struct kvm_memory_slot *old,
                                const struct kvm_memory_slot *new,
                                enum kvm_mr_change change)
 {
-       struct kvm_s390_mmu_cache *mc = NULL;
+       const struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, };
+       struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
        int rc = 0;
 
+       guard(mutex)(&kvm->slots_arch_lock);
+
        if (change == KVM_MR_FLAGS_ONLY)
                return;
 
@@ -5830,6 +5846,12 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
        }
 
        scoped_guard(write_lock, &kvm->mmu_lock) {
+               if (kvm->arch.migration_mode && kvm->arch.use_cmma && old) {
+                       _dat_walk_gfn_range(old->base_gfn, old->base_gfn + old->npages,
+                                           kvm->arch.gmap->asce, &ops, DAT_WALK_IGN_HOLES,
+                                           &kvm->arch.cmma_dirty_pages);
+               }
+
                switch (change) {
                case KVM_MR_DELETE:
                        rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
@@ -5851,7 +5873,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
 out:
        if (rc)
                pr_warn("failed to commit memory region\n");
-       kvm_s390_free_mmu_cache(mc);
        return;
 }