mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
KVM: s390: cmma: Fix dirty tracking when removing memslot
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: e38c884df9 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Message-ID: <20260803124040.126471-9-imbrenda@linux.ibm.com>
This commit is contained in:
parent
4db7207052
commit
ec21534627
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user