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: 19d6c5b804 ("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>
master
parent
bfe62a4545
commit
3ffe5eb4a5
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue