Skip to content

Commit 919efca

Browse files
Gautam Galagregkh
authored andcommitted
KVM: s390: Fix to clear PTE when discarding a swapped page
commit 5deafa2 upstream. 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: 2001979 ("KVM: s390: Refactor and split some gmap helpers") Cc: [email protected] Suggested-by: Claudio Imbrenda <[email protected]> Signed-off-by: Gautam Gala <[email protected]> Reviewed-by: Claudio Imbrenda <[email protected]> Signed-off-by: Claudio Imbrenda <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 3d0dc6c commit 919efca

File tree

3 files changed

+34
-23
lines changed

3 files changed

+34
-23
lines changed

arch/s390/include/asm/pgtable.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,4 +2055,26 @@ static inline unsigned long gmap_pgste_get_pgt_addr(unsigned long *pgt)
20552055
return res;
20562056
}
20572057

2058+
static inline pgste_t pgste_get_lock(pte_t *ptep)
2059+
{
2060+
unsigned long value = 0;
2061+
#ifdef CONFIG_PGSTE
2062+
unsigned long *ptr = (unsigned long *)(ptep + PTRS_PER_PTE);
2063+
2064+
do {
2065+
value = __atomic64_or_barrier(PGSTE_PCL_BIT, ptr);
2066+
} while (value & PGSTE_PCL_BIT);
2067+
value |= PGSTE_PCL_BIT;
2068+
#endif
2069+
return __pgste(value);
2070+
}
2071+
2072+
static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste)
2073+
{
2074+
#ifdef CONFIG_PGSTE
2075+
barrier();
2076+
WRITE_ONCE(*(unsigned long *)(ptep + PTRS_PER_PTE), pgste_val(pgste) & ~PGSTE_PCL_BIT);
2077+
#endif
2078+
}
2079+
20582080
#endif /* _S390_PAGE_H */

arch/s390/mm/gmap_helpers.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/pagewalk.h>
1616
#include <linux/ksm.h>
1717
#include <asm/gmap_helpers.h>
18+
#include <asm/pgtable.h>
1819

1920
/**
2021
* ptep_zap_swap_entry() - discard a swap entry.
@@ -47,6 +48,7 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr)
4748
{
4849
struct vm_area_struct *vma;
4950
spinlock_t *ptl;
51+
pgste_t pgste;
5052
pte_t *ptep;
5153

5254
mmap_assert_locked(mm);
@@ -60,8 +62,16 @@ void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr)
6062
ptep = get_locked_pte(mm, vmaddr, &ptl);
6163
if (unlikely(!ptep))
6264
return;
63-
if (pte_swap(*ptep))
65+
if (pte_swap(*ptep)) {
66+
preempt_disable();
67+
pgste = pgste_get_lock(ptep);
68+
6469
ptep_zap_swap_entry(mm, pte_to_swp_entry(*ptep));
70+
pte_clear(mm, vmaddr, ptep);
71+
72+
pgste_set_unlock(ptep, pgste);
73+
preempt_enable();
74+
}
6575
pte_unmap_unlock(ptep, ptl);
6676
}
6777
EXPORT_SYMBOL_GPL(gmap_helper_zap_one_page);

arch/s390/mm/pgtable.c

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <asm/tlbflush.h>
2525
#include <asm/mmu_context.h>
2626
#include <asm/page-states.h>
27+
#include <asm/pgtable.h>
2728
#include <asm/machine.h>
2829

2930
pgprot_t pgprot_writecombine(pgprot_t prot)
@@ -115,28 +116,6 @@ static inline pte_t ptep_flush_lazy(struct mm_struct *mm,
115116
return old;
116117
}
117118

118-
static inline pgste_t pgste_get_lock(pte_t *ptep)
119-
{
120-
unsigned long value = 0;
121-
#ifdef CONFIG_PGSTE
122-
unsigned long *ptr = (unsigned long *)(ptep + PTRS_PER_PTE);
123-
124-
do {
125-
value = __atomic64_or_barrier(PGSTE_PCL_BIT, ptr);
126-
} while (value & PGSTE_PCL_BIT);
127-
value |= PGSTE_PCL_BIT;
128-
#endif
129-
return __pgste(value);
130-
}
131-
132-
static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste)
133-
{
134-
#ifdef CONFIG_PGSTE
135-
barrier();
136-
WRITE_ONCE(*(unsigned long *)(ptep + PTRS_PER_PTE), pgste_val(pgste) & ~PGSTE_PCL_BIT);
137-
#endif
138-
}
139-
140119
static inline pgste_t pgste_get(pte_t *ptep)
141120
{
142121
unsigned long pgste = 0;

0 commit comments

Comments
 (0)