[PATCH v6 14/14] KVM: s390: Move DAT table updates to kvm_arch_prepare_memory_region()
From: Claudio Imbrenda
Date: Thu Jul 30 2026 - 14:01:29 EST
Currently, when a memslot is changed, the changes in the page tables
are performed in kvm_arch_commit_memory_region(), which is assumed to
never fail, as it cannot return an error code.
If memory is tight, the allocation of page tables can fail, a warning
is printed, and the system is left in an inconsistent state.
Move the whole page table update into kvm_arch_prepare_memory_region()
instead. This function can fail and return an appropriate error code,
thus not leaving the system in an inconsistent state.
Fixes: 598841ca9919 ("[S390] use gmap address spaces for kvm guest images")
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@xxxxxxxxxxxxx>
---
arch/s390/kvm/kvm-s390.c | 87 ++++++++++++++++++----------------------
1 file changed, 40 insertions(+), 47 deletions(-)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 035476ec7fbc..668272dffa18 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -5761,11 +5761,30 @@ bool kvm_arch_irqchip_in_kernel(struct kvm *kvm)
}
/* Section: memory related */
+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;
+}
+
int kvm_arch_prepare_memory_region(struct kvm *kvm,
const struct kvm_memory_slot *old,
struct kvm_memory_slot *new,
enum kvm_mr_change change)
{
+ 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 = -ENOMEM;
+
+ lockdep_assert_held(&kvm->slots_arch_lock);
+
if (kvm_is_ucontrol(kvm) && new && new->id < KVM_USER_MEM_SLOTS)
return -EINVAL;
@@ -5788,55 +5807,27 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
return -EINVAL;
}
- if (!kvm->arch.migration_mode)
- return 0;
-
- /*
- * Turn off migration mode when:
- * - userspace creates a new memslot with dirty logging off,
- * - userspace modifies an existing memslot (MOVE or FLAGS_ONLY) and
- * dirty logging is turned off.
- * Migration mode expects dirty page logging being enabled to store
- * its dirty bitmap.
- */
- if (change != KVM_MR_DELETE &&
- !(new->flags & KVM_MEM_LOG_DIRTY_PAGES))
- WARN(kvm_s390_vm_stop_migration(kvm),
- "Failed to stop migration mode");
-
- 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);
+ if (kvm->arch.migration_mode) {
+ /*
+ * Turn off migration mode when:
+ * - userspace creates a new memslot with dirty logging off,
+ * - userspace modifies an existing memslot (MOVE or FLAGS_ONLY)
+ * and dirty logging is turned off.
+ * Migration mode expects dirty page logging being enabled to
+ * store its dirty bitmap.
+ */
+ if (change != KVM_MR_DELETE &&
+ !(new->flags & KVM_MEM_LOG_DIRTY_PAGES))
+ WARN(kvm_s390_vm_stop_migration(kvm),
+ "Failed to stop migration mode");
}
- 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)
-{
- 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 = -ENOMEM;
-
- guard(mutex)(&kvm->slots_arch_lock);
if (change == KVM_MR_FLAGS_ONLY)
- return;
+ return 0;
mc = kvm_s390_new_mmu_cache();
if (!mc)
- goto out;
+ return -ENOMEM;
retry:
scoped_guard(write_lock, &kvm->mmu_lock) {
if (kvm->arch.migration_mode && kvm->arch.use_cmma && old) {
@@ -5869,10 +5860,12 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
if (!rc)
goto retry;
}
-out:
- if (KVM_BUG_ON(rc, kvm))
- pr_warn("failed to commit memory region\n");
- return;
+ return rc;
+}
+
+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)
+{
}
/**
--
2.55.0