Skip to content

Commit

Permalink
powerpc/mm: handle protnone ptes on fork
Browse files Browse the repository at this point in the history
We need to mark pages of parent process read only on fork.  Numa fault
pte needs a protnone ptes variant with saved write flag set.  On fork we
need to make sure we remove the saved write bit.  Instead of adding the
protnone check in the caller update ptep_set_wrprotect variants to clear
savedwrite bit.

Without this we see random segfaults in application on fork.

Fixes: c137a27 ("powerpc/mm/autonuma: switch ppc64 to its own implementation of saved write")
Link: http://lkml.kernel.org/r/1488203787-17849-1-git-send-email-aneesh.kumar@linux.vnet.ibm.com
Signed-off-by: Aneesh Kumar K.V <[email protected]>
Cc: Rik van Riel <[email protected]>
Cc: Mel Gorman <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Michael Ellerman <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
kvaneesh authored and torvalds committed Mar 10, 2017
1 parent 505d308 commit 52c50ca
Showing 1 changed file with 42 additions and 31 deletions.
73 changes: 42 additions & 31 deletions arch/powerpc/include/asm/book3s/64/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,23 +347,53 @@ static inline int __ptep_test_and_clear_young(struct mm_struct *mm,
__r; \
})

static inline int pte_write(pte_t pte)
{
return !!(pte_raw(pte) & cpu_to_be64(_PAGE_WRITE));
}

#ifdef CONFIG_NUMA_BALANCING
#define pte_savedwrite pte_savedwrite
static inline bool pte_savedwrite(pte_t pte)
{
/*
* Saved write ptes are prot none ptes that doesn't have
* privileged bit sit. We mark prot none as one which has
* present and pviliged bit set and RWX cleared. To mark
* protnone which used to have _PAGE_WRITE set we clear
* the privileged bit.
*/
return !(pte_raw(pte) & cpu_to_be64(_PAGE_RWX | _PAGE_PRIVILEGED));
}
#else
#define pte_savedwrite pte_savedwrite
static inline bool pte_savedwrite(pte_t pte)
{
return false;
}
#endif

#define __HAVE_ARCH_PTEP_SET_WRPROTECT
static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr,
pte_t *ptep)
{
if ((pte_raw(*ptep) & cpu_to_be64(_PAGE_WRITE)) == 0)
return;

pte_update(mm, addr, ptep, _PAGE_WRITE, 0, 0);
if (pte_write(*ptep))
pte_update(mm, addr, ptep, _PAGE_WRITE, 0, 0);
else if (unlikely(pte_savedwrite(*ptep)))
pte_update(mm, addr, ptep, 0, _PAGE_PRIVILEGED, 0);
}

static inline void huge_ptep_set_wrprotect(struct mm_struct *mm,
unsigned long addr, pte_t *ptep)
{
if ((pte_raw(*ptep) & cpu_to_be64(_PAGE_WRITE)) == 0)
return;

pte_update(mm, addr, ptep, _PAGE_WRITE, 0, 1);
/*
* We should not find protnone for hugetlb, but this complete the
* interface.
*/
if (pte_write(*ptep))
pte_update(mm, addr, ptep, _PAGE_WRITE, 0, 1);
else if (unlikely(pte_savedwrite(*ptep)))
pte_update(mm, addr, ptep, 0, _PAGE_PRIVILEGED, 1);
}

#define __HAVE_ARCH_PTEP_GET_AND_CLEAR
Expand Down Expand Up @@ -397,11 +427,6 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr,
pte_update(mm, addr, ptep, ~0UL, 0, 0);
}

static inline int pte_write(pte_t pte)
{
return !!(pte_raw(pte) & cpu_to_be64(_PAGE_WRITE));
}

static inline int pte_dirty(pte_t pte)
{
return !!(pte_raw(pte) & cpu_to_be64(_PAGE_DIRTY));
Expand Down Expand Up @@ -466,19 +491,6 @@ static inline pte_t pte_clear_savedwrite(pte_t pte)
return __pte(pte_val(pte) | _PAGE_PRIVILEGED);
}

#define pte_savedwrite pte_savedwrite
static inline bool pte_savedwrite(pte_t pte)
{
/*
* Saved write ptes are prot none ptes that doesn't have
* privileged bit sit. We mark prot none as one which has
* present and pviliged bit set and RWX cleared. To mark
* protnone which used to have _PAGE_WRITE set we clear
* the privileged bit.
*/
VM_BUG_ON(!pte_protnone(pte));
return !(pte_raw(pte) & cpu_to_be64(_PAGE_RWX | _PAGE_PRIVILEGED));
}
#endif /* CONFIG_NUMA_BALANCING */

static inline int pte_present(pte_t pte)
Expand Down Expand Up @@ -982,11 +994,10 @@ static inline int __pmdp_test_and_clear_young(struct mm_struct *mm,
static inline void pmdp_set_wrprotect(struct mm_struct *mm, unsigned long addr,
pmd_t *pmdp)
{

if ((pmd_raw(*pmdp) & cpu_to_be64(_PAGE_WRITE)) == 0)
return;

pmd_hugepage_update(mm, addr, pmdp, _PAGE_WRITE, 0);
if (pmd_write((*pmdp)))
pmd_hugepage_update(mm, addr, pmdp, _PAGE_WRITE, 0);
else if (unlikely(pmd_savedwrite(*pmdp)))
pmd_hugepage_update(mm, addr, pmdp, 0, _PAGE_PRIVILEGED);
}

static inline int pmd_trans_huge(pmd_t pmd)
Expand Down

0 comments on commit 52c50ca

Please sign in to comment.