]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: s390: vsie: Fix races with partial gmap invalidations
authorClaudio Imbrenda <imbrenda@linux.ibm.com>
Tue, 7 Apr 2026 16:17:21 +0000 (18:17 +0200)
committerClaudio Imbrenda <imbrenda@linux.ibm.com>
Tue, 7 Apr 2026 16:20:58 +0000 (18:20 +0200)
Introduce a new boolean flag, used for shadow gmaps, to keep track of
whether the gmap has been invalidated, either partially or totally.

Use the new flag to check whether shadow gmap invalidations happened
during shadowing. In such cases, abort whatever was going on, return
-EAGAIN and let the caller try again.

Fixes: 19d6c5b80443 ("KVM: s390: vsie: Fix unshadowing while shadowing")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Message-ID: <20260407161721.247044-1-imbrenda@linux.ibm.com>

arch/s390/kvm/gaccess.c
arch/s390/kvm/gmap.c
arch/s390/kvm/gmap.h

index 53a8550e7102e852ac583ad8c0ccb5818e8d5096..290e03a13a956277abe8cb888dbdf10bcd81a8e5 100644 (file)
@@ -1449,7 +1449,7 @@ static int _do_shadow_pte(struct gmap *sg, gpa_t raddr, union pte *ptep_h, union
        pgste_set_unlock(ptep_h, pgste);
        if (rc)
                return rc;
-       if (!sg->parent)
+       if (sg->invalidated)
                return -EAGAIN;
 
        newpte = _pte(f->pfn, 0, !p, 0);
@@ -1479,7 +1479,7 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni
 
        do {
                /* _gmap_crstep_xchg_atomic() could have unshadowed this shadow gmap */
-               if (!sg->parent)
+               if (sg->invalidated)
                        return -EAGAIN;
                oldcrste = READ_ONCE(*host);
                newcrste = _crste_fc1(f->pfn, oldcrste.h.tt, f->writable, !p);
@@ -1492,7 +1492,7 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni
                if (!newcrste.h.p && !f->writable)
                        return -EOPNOTSUPP;
        } while (!_gmap_crstep_xchg_atomic(sg->parent, host, oldcrste, newcrste, f->gfn, false));
-       if (!sg->parent)
+       if (sg->invalidated)
                return -EAGAIN;
 
        newcrste = _crste_fc1(f->pfn, oldcrste.h.tt, 0, !p);
@@ -1545,7 +1545,7 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg,
                                       entries[i].pfn, i + 1, entries[i].writable);
                if (rc)
                        return rc;
-               if (!sg->parent)
+               if (sg->invalidated)
                        return -EAGAIN;
        }
 
@@ -1601,6 +1601,7 @@ again:
                scoped_guard(spinlock, &parent->children_lock) {
                        if (READ_ONCE(sg->parent) != parent)
                                return -EAGAIN;
+                       sg->invalidated = false;
                        rc = _gaccess_do_shadow(vcpu->arch.mc, sg, saddr, walk);
                }
                if (rc == -ENOMEM)
index 645c32c767d24b81c401028a6679224e67e70eed..0111d31e038656da5672bd7dd88fce71cd89f7ad 100644 (file)
@@ -181,6 +181,7 @@ void gmap_remove_child(struct gmap *child)
 
        list_del(&child->list);
        child->parent = NULL;
+       child->invalidated = true;
 }
 
 /**
@@ -1069,6 +1070,7 @@ static void gmap_unshadow_level(struct gmap *sg, gfn_t r_gfn, int level)
        if (level > TABLE_TYPE_PAGE_TABLE)
                align = 1UL << (11 * level + _SEGMENT_SHIFT);
        kvm_s390_vsie_gmap_notifier(sg, ALIGN_DOWN(gaddr, align), ALIGN(gaddr + 1, align));
+       sg->invalidated = true;
        if (dat_entry_walk(NULL, r_gfn, sg->asce, 0, level, &crstep, &ptep))
                return;
        if (ptep) {
@@ -1174,6 +1176,7 @@ static inline int __gmap_protect_asce_top_level(struct kvm_s390_mmu_cache *mc, s
        scoped_guard(spinlock, &parent->children_lock) {
                if (READ_ONCE(sg->parent) != parent)
                        return -EAGAIN;
+               sg->invalidated = false;
                for (i = 0; i < CRST_TABLE_PAGES; i++) {
                        if (!context->f[i].valid)
                                continue;
index 579399ef54803d85f1bad8f7751ebf72efd83569..31ea13fda142bc945ef0cd48cca65d3b74968f72 100644 (file)
@@ -60,6 +60,7 @@ enum gmap_flags {
 struct gmap {
        unsigned long flags;
        unsigned char edat_level;
+       bool invalidated;
        struct kvm *kvm;
        union asce asce;
        struct list_head list;