* [PATCH v1 0/7] KVM: s390: More gmap and vsie fixes
@ 2026-05-28 11:47 Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 1/7] KVM: s390: Fix _gmap_crstep_xchg_atomic() Claudio Imbrenda
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Claudio Imbrenda @ 2026-05-28 11:47 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
Another batch of fixups for gmap and vsie. Some minor fixes, some
not-so-minor fixes that could have caused guest corruption under
particular circumstances.
Claudio Imbrenda (7):
KVM: s390: Fix _gmap_crstep_xchg_atomic()
KVM: s390: Fix guest / virtual address confusion in _essa_clear_cbrl()
KVM: s390: vsie: Fix rmap handling in _do_shadow_crste()
KVM: s390: Fix fault-in code
KVM: s390: Avoid potentially sleeping while atomic when zapping pages
KVM: s390: Lock pte when making page secure
KVM: s390: Prevent memslots outside the ASCE range
arch/s390/include/asm/gmap_helpers.h | 1 +
arch/s390/kvm/faultin.c | 13 ++--
arch/s390/kvm/gaccess.c | 11 +--
arch/s390/kvm/gmap.h | 2 +-
arch/s390/kvm/kvm-s390.c | 24 +++++-
arch/s390/kvm/priv.c | 8 +-
arch/s390/kvm/pv.c | 15 +++-
arch/s390/mm/gmap_helpers.c | 111 ++++++++++++++++-----------
8 files changed, 121 insertions(+), 64 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 1/7] KVM: s390: Fix _gmap_crstep_xchg_atomic()
2026-05-28 11:47 [PATCH v1 0/7] KVM: s390: More gmap and vsie fixes Claudio Imbrenda
@ 2026-05-28 11:47 ` Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 2/7] KVM: s390: Fix guest / virtual address confusion in _essa_clear_cbrl() Claudio Imbrenda
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Claudio Imbrenda @ 2026-05-28 11:47 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
Return false and do not perform the operation if an unshadow event has
been triggered.
The previous incorrect behaviour cleared the vsie_notif bit without
returning false, which allowed shadow crstes to be installed without
the vsie_notif bit.
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Fixes: b827ef02f409 ("KVM: s390: Remove non-atomic dat_crstep_xchg()")
Fixes: a2c17f9270cc ("KVM: s390: New gmap code")
---
arch/s390/kvm/gmap.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/s390/kvm/gmap.h b/arch/s390/kvm/gmap.h
index 742e42a31744..122ed8566314 100644
--- a/arch/s390/kvm/gmap.h
+++ b/arch/s390/kvm/gmap.h
@@ -273,11 +273,11 @@ static inline bool __must_check _gmap_crstep_xchg_atomic(struct gmap *gmap, unio
gmap_unmap_prefix(gmap, gfn, gfn + align);
}
if (crste_leaf(oldcrste) && crste_needs_unshadow(oldcrste, newcrste)) {
- newcrste.s.fc1.vsie_notif = 0;
if (needs_lock)
gmap_handle_vsie_unshadow_event(gmap, gfn);
else
_gmap_handle_vsie_unshadow_event(gmap, gfn);
+ return false;
}
if (!oldcrste.s.fc1.d && newcrste.s.fc1.d && !newcrste.s.fc1.s)
SetPageDirty(phys_to_page(crste_origin_large(newcrste)));
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 2/7] KVM: s390: Fix guest / virtual address confusion in _essa_clear_cbrl()
2026-05-28 11:47 [PATCH v1 0/7] KVM: s390: More gmap and vsie fixes Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 1/7] KVM: s390: Fix _gmap_crstep_xchg_atomic() Claudio Imbrenda
@ 2026-05-28 11:47 ` Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 3/7] KVM: s390: vsie: Fix rmap handling in _do_shadow_crste() Claudio Imbrenda
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Claudio Imbrenda @ 2026-05-28 11:47 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
Until now, gmap_helper_zap_one_page() was being called with the guest
absolute address, but it expects a userspace virtual address.
This meant that in the best case the requested pages were not being
discarded, and in the worst case that the wrong pages were being
discarded.
Fix this by converting the guest absolute address to host virtual
before passing it to gmap_helper_zap_one_page().
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
---
arch/s390/kvm/priv.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index cc0553da14cb..447ec7ed423d 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -1188,6 +1188,7 @@ static void _essa_clear_cbrl(struct kvm_vcpu *vcpu, unsigned long *cbrl, int len
union crste *crstep;
union pgste pgste;
union pte *ptep;
+ hva_t hva;
int i;
lockdep_assert_held(&vcpu->kvm->mmu_lock);
@@ -1199,8 +1200,11 @@ static void _essa_clear_cbrl(struct kvm_vcpu *vcpu, unsigned long *cbrl, int len
if (!ptep || ptep->s.pr)
continue;
pgste = pgste_get_lock(ptep);
- if (pgste.usage == PGSTE_GPS_USAGE_UNUSED || pgste.zero)
- gmap_helper_zap_one_page(vcpu->kvm->mm, cbrl[i]);
+ if (pgste.usage == PGSTE_GPS_USAGE_UNUSED || pgste.zero) {
+ hva = gpa_to_hva(vcpu->kvm, cbrl[i]);
+ if (!kvm_is_error_hva(hva))
+ gmap_helper_zap_one_page(vcpu->kvm->mm, hva);
+ }
pgste_set_unlock(ptep, pgste);
}
}
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 3/7] KVM: s390: vsie: Fix rmap handling in _do_shadow_crste()
2026-05-28 11:47 [PATCH v1 0/7] KVM: s390: More gmap and vsie fixes Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 1/7] KVM: s390: Fix _gmap_crstep_xchg_atomic() Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 2/7] KVM: s390: Fix guest / virtual address confusion in _essa_clear_cbrl() Claudio Imbrenda
@ 2026-05-28 11:47 ` Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 4/7] KVM: s390: Fix fault-in code Claudio Imbrenda
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Claudio Imbrenda @ 2026-05-28 11:47 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
Fix _do_shadow_crste() to also apply a mask on the reverse address, to
prevent spurious entries from being created, like already done in
gmap_protect_rmap().
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
---
arch/s390/kvm/gaccess.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c
index 4f8d5592c9a9..20e28b183c1a 100644
--- a/arch/s390/kvm/gaccess.c
+++ b/arch/s390/kvm/gaccess.c
@@ -1466,15 +1466,17 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni
struct guest_fault *f, bool p)
{
union crste newcrste, oldcrste;
- gfn_t gfn;
+ unsigned long mask;
+ gfn_t r_gfn;
int rc;
lockdep_assert_held(&sg->kvm->mmu_lock);
lockdep_assert_held(&sg->parent->children_lock);
- gfn = f->gfn & (is_pmd(*table) ? _SEGMENT_FR_MASK : _REGION3_FR_MASK);
+ mask = is_pmd(*table) ? _SEGMENT_FR_MASK : _REGION3_FR_MASK;
+ r_gfn = gpa_to_gfn(raddr) & mask;
scoped_guard(spinlock, &sg->host_to_rmap_lock)
- rc = gmap_insert_rmap(sg, gfn, gpa_to_gfn(raddr), host->h.tt);
+ rc = gmap_insert_rmap(sg, f->gfn & mask, r_gfn, host->h.tt);
if (rc)
return rc;
@@ -1497,8 +1499,7 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni
return -EAGAIN;
newcrste = _crste_fc1(f->pfn, oldcrste.h.tt, 0, !p);
- gfn = gpa_to_gfn(raddr);
- while (!dat_crstep_xchg_atomic(table, READ_ONCE(*table), newcrste, gfn, sg->asce))
+ while (!dat_crstep_xchg_atomic(table, READ_ONCE(*table), newcrste, r_gfn, sg->asce))
;
return 0;
}
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 4/7] KVM: s390: Fix fault-in code
2026-05-28 11:47 [PATCH v1 0/7] KVM: s390: More gmap and vsie fixes Claudio Imbrenda
` (2 preceding siblings ...)
2026-05-28 11:47 ` [PATCH v1 3/7] KVM: s390: vsie: Fix rmap handling in _do_shadow_crste() Claudio Imbrenda
@ 2026-05-28 11:47 ` Claudio Imbrenda
2026-05-28 14:08 ` Steffen Eiden
2026-05-28 11:47 ` [PATCH v1 5/7] KVM: s390: Avoid potentially sleeping while atomic when zapping pages Claudio Imbrenda
` (2 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Claudio Imbrenda @ 2026-05-28 11:47 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
Fix the fault-in code so that it does not return success if a
concurrent unmap event invalidated the fault-in process between the
best-effort lockless check and the proper check with lock.
The new behaviour is to retry, like the best-effort lockless check
already did.
This prevents the fault-in handler from returning success without
having actually faulted in the requested page.
Fixes: e907ae530133 ("KVM: s390: Add helper functions for fault handling")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
---
arch/s390/kvm/faultin.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/arch/s390/kvm/faultin.c b/arch/s390/kvm/faultin.c
index ddf0ca71f374..3047dfdc8be4 100644
--- a/arch/s390/kvm/faultin.c
+++ b/arch/s390/kvm/faultin.c
@@ -36,7 +36,7 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa
struct kvm_s390_mmu_cache *mc = NULL;
struct kvm_memory_slot *slot;
unsigned long inv_seq;
- int foll, rc = 0;
+ int foll, rc = -EAGAIN;
foll = f->write_attempt ? FOLL_WRITE : 0;
foll |= f->attempt_pfault ? FOLL_NOWAIT : 0;
@@ -53,7 +53,7 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa
return 0;
}
- while (1) {
+ while (rc == -EAGAIN) {
f->valid = false;
inv_seq = kvm->mmu_invalidate_seq;
/* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */
@@ -110,20 +110,19 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa
if (!mmu_invalidate_retry_gfn(kvm, inv_seq, f->gfn)) {
f->valid = true;
rc = gmap_link(mc, kvm->arch.gmap, f, slot);
- kvm_release_faultin_page(kvm, f->page, !!rc, f->write_attempt);
- f->page = NULL;
}
+ kvm_release_faultin_page(kvm, f->page, !!rc, f->write_attempt);
}
- kvm_release_faultin_page(kvm, f->page, true, false);
if (rc == -ENOMEM) {
rc = kvm_s390_mmu_cache_topup(mc);
if (rc)
return rc;
- } else if (rc != -EAGAIN) {
- return rc;
+ rc = -EAGAIN;
}
}
+
+ return rc;
}
int kvm_s390_get_guest_page(struct kvm *kvm, struct guest_fault *f, gfn_t gfn, bool w)
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 5/7] KVM: s390: Avoid potentially sleeping while atomic when zapping pages
2026-05-28 11:47 [PATCH v1 0/7] KVM: s390: More gmap and vsie fixes Claudio Imbrenda
` (3 preceding siblings ...)
2026-05-28 11:47 ` [PATCH v1 4/7] KVM: s390: Fix fault-in code Claudio Imbrenda
@ 2026-05-28 11:47 ` Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 6/7] KVM: s390: Lock pte when making page secure Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 7/7] KVM: s390: Prevent memslots outside the ASCE range Claudio Imbrenda
6 siblings, 0 replies; 9+ messages in thread
From: Claudio Imbrenda @ 2026-05-28 11:47 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
Factor out try_get_locked_pte(), which behaves similarly to
get_locked_pte(), but does not attempt to allocate missing tables and
performs a spin_trylock() instead of blocking.
The new function is also exported, since it will be used in other
patches.
If intermediate entries are missing, there can be no pte swap entry to
free, so it's safe to ignore them.
This avoids potentially sleeping while atomic.
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
---
arch/s390/include/asm/gmap_helpers.h | 1 +
arch/s390/mm/gmap_helpers.c | 111 ++++++++++++++++-----------
2 files changed, 68 insertions(+), 44 deletions(-)
diff --git a/arch/s390/include/asm/gmap_helpers.h b/arch/s390/include/asm/gmap_helpers.h
index 2d3ae421077e..d2b616604a46 100644
--- a/arch/s390/include/asm/gmap_helpers.h
+++ b/arch/s390/include/asm/gmap_helpers.h
@@ -12,5 +12,6 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr);
void gmap_helper_discard(struct mm_struct *mm, unsigned long vmaddr, unsigned long end);
int gmap_helper_disable_cow_sharing(void);
void gmap_helper_try_set_pte_unused(struct mm_struct *mm, unsigned long vmaddr);
+pte_t *try_get_locked_pte(struct mm_struct *mm, unsigned long addr, spinlock_t **ptl);
#endif /* _ASM_S390_GMAP_HELPERS_H */
diff --git a/arch/s390/mm/gmap_helpers.c b/arch/s390/mm/gmap_helpers.c
index f8789ffcc05c..7ba15f307bb0 100644
--- a/arch/s390/mm/gmap_helpers.c
+++ b/arch/s390/mm/gmap_helpers.c
@@ -34,6 +34,66 @@ static void ptep_zap_softleaf_entry(struct mm_struct *mm, softleaf_t entry)
swap_put_entries_direct(entry, 1);
}
+/**
+ * try_get_locked_pte() - like get_locked_pte(), but atomic and with trylock
+ * @mm: the mm
+ * @vmaddr: the userspace virtual address whose pte is to be found
+ * @ptl: will be set to the pointer to the lock used to lock the pte in case
+ * of success.
+ *
+ * This function returns the pointer to the pte corresponding to @addr in @mm,
+ * similarly to get_locked_pte(). Unlike get_locked_pte(), no attempt is made
+ * to allocate missing page tables. If a missing or large entry is found, the
+ * function will return NULL. If the ptl lock is contended, NULL is returned.
+ *
+ * In case of success, *@ptl will point to the locked pte lock for the returned
+ * pte, like get_locked_pte() does.
+ *
+ * Context: mmap_lock or vma lock for read or for write needs to be held.
+ * Return: the pointer to the pte corresponding to @addr in @mm, if possible,
+ * otherwise NULL.
+ */
+pte_t *try_get_locked_pte(struct mm_struct *mm, unsigned long vmaddr, spinlock_t **ptl)
+{
+ pmd_t *pmdp, pmd, pmdval;
+ pud_t *pudp, pud;
+ p4d_t *p4dp, p4d;
+ pgd_t *pgdp, pgd;
+ pte_t *ptep;
+
+ pgdp = pgd_offset(mm, vmaddr);
+ pgd = pgdp_get(pgdp);
+ if (pgd_none(pgd) || !pgd_present(pgd))
+ return NULL;
+ p4dp = p4d_offset(pgdp, vmaddr);
+ p4d = p4dp_get(p4dp);
+ if (p4d_none(p4d) || !p4d_present(p4d))
+ return NULL;
+ pudp = pud_offset(p4dp, vmaddr);
+ pud = pudp_get(pudp);
+ if (pud_none(pud) || pud_leaf(pud) || !pud_present(pud))
+ return NULL;
+ pmdp = pmd_offset(pudp, vmaddr);
+ pmd = pmdp_get_lockless(pmdp);
+ if (pmd_none(pmd) || pmd_leaf(pmd) || !pmd_present(pmd))
+ return NULL;
+ ptep = pte_offset_map_rw_nolock(mm, pmdp, vmaddr, &pmdval, ptl);
+ if (!ptep)
+ return NULL;
+
+ if (spin_trylock(*ptl)) {
+ if (unlikely(!pmd_same(pmdval, pmdp_get_lockless(pmdp)))) {
+ pte_unmap_unlock(ptep, *ptl);
+ return NULL;
+ }
+ return ptep;
+ }
+
+ pte_unmap(ptep);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(try_get_locked_pte);
+
/**
* gmap_helper_zap_one_page() - discard a page if it was swapped.
* @mm: the mm
@@ -46,7 +106,7 @@ static void ptep_zap_softleaf_entry(struct mm_struct *mm, softleaf_t entry)
void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr)
{
struct vm_area_struct *vma;
- spinlock_t *ptl;
+ spinlock_t *ptl; /* Lock for the host (userspace) page table */
pte_t *ptep;
mmap_assert_locked(mm);
@@ -57,7 +117,7 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr)
return;
/* Get pointer to the page table entry */
- ptep = get_locked_pte(mm, vmaddr, &ptl);
+ ptep = try_get_locked_pte(mm, vmaddr, &ptl);
if (unlikely(!ptep))
return;
if (pte_swap(*ptep)) {
@@ -113,37 +173,9 @@ EXPORT_SYMBOL_GPL(gmap_helper_discard);
*/
void gmap_helper_try_set_pte_unused(struct mm_struct *mm, unsigned long vmaddr)
{
- pmd_t *pmdp, pmd, pmdval;
- pud_t *pudp, pud;
- p4d_t *p4dp, p4d;
- pgd_t *pgdp, pgd;
spinlock_t *ptl; /* Lock for the host (userspace) page table */
pte_t *ptep;
- pgdp = pgd_offset(mm, vmaddr);
- pgd = pgdp_get(pgdp);
- if (pgd_none(pgd) || !pgd_present(pgd))
- return;
-
- p4dp = p4d_offset(pgdp, vmaddr);
- p4d = p4dp_get(p4dp);
- if (p4d_none(p4d) || !p4d_present(p4d))
- return;
-
- pudp = pud_offset(p4dp, vmaddr);
- pud = pudp_get(pudp);
- if (pud_none(pud) || pud_leaf(pud) || !pud_present(pud))
- return;
-
- pmdp = pmd_offset(pudp, vmaddr);
- pmd = pmdp_get_lockless(pmdp);
- if (pmd_none(pmd) || pmd_leaf(pmd) || !pmd_present(pmd))
- return;
-
- ptep = pte_offset_map_rw_nolock(mm, pmdp, vmaddr, &pmdval, &ptl);
- if (!ptep)
- return;
-
/*
* Several paths exists that takes the ptl lock and then call the
* mmu_notifier, which takes the mmu_lock. The unmap path, instead,
@@ -156,21 +188,12 @@ void gmap_helper_try_set_pte_unused(struct mm_struct *mm, unsigned long vmaddr)
* If the lock is contended the bit is not set and the deadlock is
* avoided.
*/
- if (spin_trylock(ptl)) {
- /*
- * Make sure the pte we are touching is still the correct
- * one. In theory this check should not be needed, but
- * better safe than sorry.
- * Disabling interrupts or holding the mmap lock is enough to
- * guarantee that no concurrent updates to the page tables
- * are possible.
- */
- if (likely(pmd_same(pmdval, pmdp_get_lockless(pmdp))))
- __atomic64_or(_PAGE_UNUSED, (long *)ptep);
- spin_unlock(ptl);
- }
+ ptep = try_get_locked_pte(mm, vmaddr, &ptl);
+ if (!ptep)
+ return;
- pte_unmap(ptep);
+ __atomic64_or(_PAGE_UNUSED, (long *)ptep);
+ pte_unmap_unlock(ptep, ptl);
}
EXPORT_SYMBOL_GPL(gmap_helper_try_set_pte_unused);
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 6/7] KVM: s390: Lock pte when making page secure
2026-05-28 11:47 [PATCH v1 0/7] KVM: s390: More gmap and vsie fixes Claudio Imbrenda
` (4 preceding siblings ...)
2026-05-28 11:47 ` [PATCH v1 5/7] KVM: s390: Avoid potentially sleeping while atomic when zapping pages Claudio Imbrenda
@ 2026-05-28 11:47 ` Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 7/7] KVM: s390: Prevent memslots outside the ASCE range Claudio Imbrenda
6 siblings, 0 replies; 9+ messages in thread
From: Claudio Imbrenda @ 2026-05-28 11:47 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
Make sure _kvm_s390_pv_make_secure() takes the pte lock for the given
address when attempting to make the page secure.
One of the steps in making the page secure is freezing the folio using
folio_ref_freeze(), which temporarily sets the reference count to 0.
Any attempt to get such a folio while frozen will fail and cause a
warning to be printed.
Other users of folio_ref_freeze() make sure that the page is not mapped
while it's being frozen, thus preventing gup functions from being able
to access it. For _kvm_s390_pv_make_secure(), this is not possible,
because the page needs to be mapped in order for the import to succeed.
By taking the pte lock, gup functions will be blocked until the import
operation is done, thus avoiding the race.
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
---
arch/s390/kvm/pv.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c
index c2dafd812a3b..3a7410f6b609 100644
--- a/arch/s390/kvm/pv.c
+++ b/arch/s390/kvm/pv.c
@@ -17,6 +17,7 @@
#include <linux/pagewalk.h>
#include <linux/sched/mm.h>
#include <linux/mmu_notifier.h>
+#include <asm/gmap_helpers.h>
#include "kvm-s390.h"
#include "dat.h"
#include "gaccess.h"
@@ -73,6 +74,7 @@ static bool should_export_before_import(struct uv_cb_header *uvcb, struct mm_str
struct pv_make_secure {
void *uvcb;
struct folio *folio;
+ struct kvm *kvm;
int rc;
bool needs_export;
};
@@ -103,17 +105,24 @@ static void _kvm_s390_pv_make_secure(struct guest_fault *f)
{
struct pv_make_secure *priv = f->priv;
struct folio *folio;
+ spinlock_t *ptl; /* pte lock from try_get_locked_pte() */
+ pte_t *ptep;
folio = pfn_folio(f->pfn);
priv->rc = -EAGAIN;
- if (folio_trylock(folio)) {
+ if (!folio_trylock(folio))
+ return;
+
+ ptep = try_get_locked_pte(priv->kvm->mm, gfn_to_hva(priv->kvm, f->gfn), &ptl);
+ if (ptep) {
priv->rc = __kvm_s390_pv_make_secure(f, folio);
if (priv->rc == -E2BIG || priv->rc == -EBUSY) {
priv->folio = folio;
folio_get(folio);
}
- folio_unlock(folio);
+ pte_unmap_unlock(ptep, ptl);
}
+ folio_unlock(folio);
}
/**
@@ -127,7 +136,7 @@ static void _kvm_s390_pv_make_secure(struct guest_fault *f)
*/
int kvm_s390_pv_make_secure(struct kvm *kvm, unsigned long gaddr, void *uvcb)
{
- struct pv_make_secure priv = { .uvcb = uvcb };
+ struct pv_make_secure priv = { .uvcb = uvcb, .kvm = kvm, };
struct guest_fault f = {
.write_attempt = true,
.gfn = gpa_to_gfn(gaddr),
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 7/7] KVM: s390: Prevent memslots outside the ASCE range
2026-05-28 11:47 [PATCH v1 0/7] KVM: s390: More gmap and vsie fixes Claudio Imbrenda
` (5 preceding siblings ...)
2026-05-28 11:47 ` [PATCH v1 6/7] KVM: s390: Lock pte when making page secure Claudio Imbrenda
@ 2026-05-28 11:47 ` Claudio Imbrenda
6 siblings, 0 replies; 9+ messages in thread
From: Claudio Imbrenda @ 2026-05-28 11:47 UTC (permalink / raw)
To: linux-kernel
Cc: kvm, linux-s390, borntraeger, frankja, david, seiden, nrb,
schlameuss, gra
With KVM_S390_VM_MEM_LIMIT_SIZE, userspace can set the highest address
allowed for the VM. Creating a memslot that lies over the maximum
address does not make sense and is only a potential source of bugs.
Prevent creation of memslots over the maximum address, and prevent the
maximum address from being reduced below the end of existing memslots.
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
---
arch/s390/kvm/kvm-s390.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index e09960c2e6ed..875f6e2a4a52 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -1015,8 +1015,26 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att
return -EINVAL;
ret = -EBUSY;
- if (!kvm->created_vcpus)
- ret = gmap_set_limit(kvm->arch.gmap, gpa_to_gfn(new_limit));
+ if (!kvm->created_vcpus) {
+ struct kvm_memslots *slots;
+ struct kvm_memory_slot *ms;
+ int bkt;
+
+ ret = 0;
+ mutex_lock(&kvm->slots_arch_lock);
+ slots = kvm_memslots(kvm);
+ if (slots && !kvm_memslots_empty(slots)) {
+ kvm_for_each_memslot(ms, bkt, slots) {
+ if (gpa_to_gfn(new_limit) < ms->base_gfn + ms->npages) {
+ ret = -EBUSY;
+ break;
+ }
+ }
+ }
+ if (!ret)
+ ret = gmap_set_limit(kvm->arch.gmap, gpa_to_gfn(new_limit));
+ mutex_unlock(&kvm->slots_arch_lock);
+ }
VM_EVENT(kvm, 3, "SET: max guest address: %lu", new_limit);
VM_EVENT(kvm, 3, "New guest asce: 0x%p",
(void *)kvm->arch.gmap->asce.val);
@@ -5672,6 +5690,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
return -EINVAL;
if ((new->base_gfn + new->npages) * PAGE_SIZE > kvm->arch.mem_limit)
return -EINVAL;
+ if (!asce_contains_gfn(kvm->arch.gmap->asce, new->base_gfn + new->npages - 1))
+ return -EINVAL;
}
if (!kvm->arch.migration_mode)
--
2.54.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v1 4/7] KVM: s390: Fix fault-in code
2026-05-28 11:47 ` [PATCH v1 4/7] KVM: s390: Fix fault-in code Claudio Imbrenda
@ 2026-05-28 14:08 ` Steffen Eiden
0 siblings, 0 replies; 9+ messages in thread
From: Steffen Eiden @ 2026-05-28 14:08 UTC (permalink / raw)
To: Claudio Imbrenda
Cc: linux-kernel, kvm, linux-s390, borntraeger, frankja, david, nrb,
schlameuss, gra
On Thu, May 28, 2026 at 01:47:24PM +0200, Claudio Imbrenda wrote:
> Fix the fault-in code so that it does not return success if a
> concurrent unmap event invalidated the fault-in process between the
> best-effort lockless check and the proper check with lock.
>
> The new behaviour is to retry, like the best-effort lockless check
> already did.
>
> This prevents the fault-in handler from returning success without
> having actually faulted in the requested page.
>
One nit below.
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
> Fixes: e907ae530133 ("KVM: s390: Add helper functions for fault handling")
> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
> ---
> arch/s390/kvm/faultin.c | 13 ++++++-------
> 1 file changed, 6 insertions(+), 7 deletions(-)
>
> diff --git a/arch/s390/kvm/faultin.c b/arch/s390/kvm/faultin.c
> index ddf0ca71f374..3047dfdc8be4 100644
> --- a/arch/s390/kvm/faultin.c
> +++ b/arch/s390/kvm/faultin.c
> @@ -36,7 +36,7 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa
> struct kvm_s390_mmu_cache *mc = NULL;
> struct kvm_memory_slot *slot;
> unsigned long inv_seq;
> - int foll, rc = 0;
> + int foll, rc = -EAGAIN;
This is not in reverse Christmas tree anymore.
Split them into two lines.
...
Steffen
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-05-28 14:08 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-28 11:47 [PATCH v1 0/7] KVM: s390: More gmap and vsie fixes Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 1/7] KVM: s390: Fix _gmap_crstep_xchg_atomic() Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 2/7] KVM: s390: Fix guest / virtual address confusion in _essa_clear_cbrl() Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 3/7] KVM: s390: vsie: Fix rmap handling in _do_shadow_crste() Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 4/7] KVM: s390: Fix fault-in code Claudio Imbrenda
2026-05-28 14:08 ` Steffen Eiden
2026-05-28 11:47 ` [PATCH v1 5/7] KVM: s390: Avoid potentially sleeping while atomic when zapping pages Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 6/7] KVM: s390: Lock pte when making page secure Claudio Imbrenda
2026-05-28 11:47 ` [PATCH v1 7/7] KVM: s390: Prevent memslots outside the ASCE range Claudio Imbrenda
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®