]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: s390: Fix to clear PTE when discarding a swapped page
authorGautam Gala <ggala@linux.ibm.com>
Wed, 24 Sep 2025 11:26:44 +0000 (13:26 +0200)
committerClaudio Imbrenda <imbrenda@linux.ibm.com>
Tue, 30 Sep 2025 13:58:30 +0000 (15:58 +0200)
KVM run fails when guests with 'cmm' cpu feature and host are
under memory pressure and use swap heavily. This is because
npages becomes ENOMEN (out of memory) in hva_to_pfn_slow()
which inturn propagates as EFAULT to qemu. Clearing the page
table entry when discarding an address that maps to a swap
entry resolves the issue.

Fixes: 200197908dc4 ("KVM: s390: Refactor and split some gmap helpers")
Cc: stable@vger.kernel.org
Suggested-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Signed-off-by: Gautam Gala <ggala@linux.ibm.com>
Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
arch/s390/include/asm/pgtable.h
arch/s390/mm/gmap_helpers.c
arch/s390/mm/pgtable.c

index 6d8bc27a366e4f17abc0e04d01dff5fcf20eb2e7..324f96485604283dc91e9b5b58c1b409c23e2674 100644 (file)
@@ -2010,4 +2010,26 @@ static inline unsigned long gmap_pgste_get_pgt_addr(unsigned long *pgt)
        return res;
 }
 
+static inline pgste_t pgste_get_lock(pte_t *ptep)
+{
+       unsigned long value = 0;
+#ifdef CONFIG_PGSTE
+       unsigned long *ptr = (unsigned long *)(ptep + PTRS_PER_PTE);
+
+       do {
+               value = __atomic64_or_barrier(PGSTE_PCL_BIT, ptr);
+       } while (value & PGSTE_PCL_BIT);
+       value |= PGSTE_PCL_BIT;
+#endif
+       return __pgste(value);
+}
+
+static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste)
+{
+#ifdef CONFIG_PGSTE
+       barrier();
+       WRITE_ONCE(*(unsigned long *)(ptep + PTRS_PER_PTE), pgste_val(pgste) & ~PGSTE_PCL_BIT);
+#endif
+}
+
 #endif /* _S390_PAGE_H */
index a45d417ad951748c9d7fe65cdce54c4e7fb74015..c382005577bdc308a519ad48bba4ecdd1194f2c6 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/pagewalk.h>
 #include <linux/ksm.h>
 #include <asm/gmap_helpers.h>
+#include <asm/pgtable.h>
 
 /**
  * ptep_zap_swap_entry() - discard a swap entry.
@@ -45,6 +46,7 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr)
 {
        struct vm_area_struct *vma;
        spinlock_t *ptl;
+       pgste_t pgste;
        pte_t *ptep;
 
        mmap_assert_locked(mm);
@@ -58,8 +60,16 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr)
        ptep = get_locked_pte(mm, vmaddr, &ptl);
        if (unlikely(!ptep))
                return;
-       if (pte_swap(*ptep))
+       if (pte_swap(*ptep)) {
+               preempt_disable();
+               pgste = pgste_get_lock(ptep);
+
                ptep_zap_swap_entry(mm, pte_to_swp_entry(*ptep));
+               pte_clear(mm, vmaddr, ptep);
+
+               pgste_set_unlock(ptep, pgste);
+               preempt_enable();
+       }
        pte_unmap_unlock(ptep, ptl);
 }
 EXPORT_SYMBOL_GPL(gmap_helper_zap_one_page);
index 7df70cd8f739ce0f96d5df86718675a128316d39..6b92c348b56f81edb611baa80237f7ba6c14a9a5 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/tlbflush.h>
 #include <asm/mmu_context.h>
 #include <asm/page-states.h>
+#include <asm/pgtable.h>
 #include <asm/machine.h>
 
 pgprot_t pgprot_writecombine(pgprot_t prot)
@@ -114,28 +115,6 @@ static inline pte_t ptep_flush_lazy(struct mm_struct *mm,
        return old;
 }
 
-static inline pgste_t pgste_get_lock(pte_t *ptep)
-{
-       unsigned long value = 0;
-#ifdef CONFIG_PGSTE
-       unsigned long *ptr = (unsigned long *)(ptep + PTRS_PER_PTE);
-
-       do {
-               value = __atomic64_or_barrier(PGSTE_PCL_BIT, ptr);
-       } while (value & PGSTE_PCL_BIT);
-       value |= PGSTE_PCL_BIT;
-#endif
-       return __pgste(value);
-}
-
-static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste)
-{
-#ifdef CONFIG_PGSTE
-       barrier();
-       WRITE_ONCE(*(unsigned long *)(ptep + PTRS_PER_PTE), pgste_val(pgste) & ~PGSTE_PCL_BIT);
-#endif
-}
-
 static inline pgste_t pgste_get(pte_t *ptep)
 {
        unsigned long pgste = 0;