summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudio Imbrenda <imbrenda@linux.ibm.com>2026-04-07 18:17:21 +0200
committerClaudio Imbrenda <imbrenda@linux.ibm.com>2026-04-07 18:20:58 +0200
commit3ffe5eb4a5f248c0d4b849f050af973396656f85 (patch)
tree3a6b6c627f42ca059de23626f1d0d1ed2199ee0b
parentbfe62a454542cfad3379f6ef5680b125f41e20f4 (diff)
KVM: s390: vsie: Fix races with partial gmap invalidations
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>
-rw-r--r--arch/s390/kvm/gaccess.c9
-rw-r--r--arch/s390/kvm/gmap.c3
-rw-r--r--arch/s390/kvm/gmap.h1
3 files changed, 9 insertions, 4 deletions
diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c
index 53a8550e7102..290e03a13a95 100644
--- a/arch/s390/kvm/gaccess.c
+++ b/arch/s390/kvm/gaccess.c
@@ -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)
diff --git a/arch/s390/kvm/gmap.c b/arch/s390/kvm/gmap.c
index 645c32c767d2..0111d31e0386 100644
--- a/arch/s390/kvm/gmap.c
+++ b/arch/s390/kvm/gmap.c
@@ -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;
diff --git a/arch/s390/kvm/gmap.h b/arch/s390/kvm/gmap.h
index 579399ef5480..31ea13fda142 100644
--- a/arch/s390/kvm/gmap.h
+++ b/arch/s390/kvm/gmap.h
@@ -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;