]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: s390: Lock pte when making page secure
authorClaudio Imbrenda <imbrenda@linux.ibm.com>
Tue, 2 Jun 2026 14:23:53 +0000 (16:23 +0200)
committerClaudio Imbrenda <imbrenda@linux.ibm.com>
Tue, 2 Jun 2026 14:46:41 +0000 (16:46 +0200)
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.

In theory this does not completely solve the issue: if a page is mapped
through multiple mappings, locking one pte does not protect from
calling gup on it through the other mapping. In practice this does not
happen and it is a decent stopgap solution until a more correct
solution is available.

Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Message-ID: <20260602142356.169458-8-imbrenda@linux.ibm.com>

arch/s390/kvm/pv.c

index c2dafd812a3b2a18e3050a96a3c7a924edf7ca06..4b865e75351c1a0bd4b0fa703685a8b428036a45 100644 (file)
@@ -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,9 +105,21 @@ 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 (!mmap_read_trylock(priv->kvm->mm))
+               return;
+
+       ptep = try_get_locked_pte(priv->kvm->mm, gfn_to_hva(priv->kvm, f->gfn), &ptl);
+       if (IS_ERR_VALUE(ptep)) {
+               priv->rc = PTR_ERR(ptep);
+               goto out;
+       }
+
        if (folio_trylock(folio)) {
                priv->rc = __kvm_s390_pv_make_secure(f, folio);
                if (priv->rc == -E2BIG || priv->rc == -EBUSY) {
@@ -114,6 +128,11 @@ static void _kvm_s390_pv_make_secure(struct guest_fault *f)
                }
                folio_unlock(folio);
        }
+
+       if (ptep)
+               pte_unmap_unlock(ptep, ptl);
+out:
+       mmap_read_unlock(priv->kvm->mm);
 }
 
 /**
@@ -127,7 +146,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),