aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/include/asm/pgtable.h33
1 files changed, 18 insertions, 15 deletions
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 149d05fb9421..3ff03a755c32 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -149,12 +149,20 @@ static inline pte_t pte_mkwrite(pte_t pte)
149 149
150static inline pte_t pte_mkclean(pte_t pte) 150static inline pte_t pte_mkclean(pte_t pte)
151{ 151{
152 return clear_pte_bit(pte, __pgprot(PTE_DIRTY)); 152 pte = clear_pte_bit(pte, __pgprot(PTE_DIRTY));
153 pte = set_pte_bit(pte, __pgprot(PTE_RDONLY));
154
155 return pte;
153} 156}
154 157
155static inline pte_t pte_mkdirty(pte_t pte) 158static inline pte_t pte_mkdirty(pte_t pte)
156{ 159{
157 return set_pte_bit(pte, __pgprot(PTE_DIRTY)); 160 pte = set_pte_bit(pte, __pgprot(PTE_DIRTY));
161
162 if (pte_write(pte))
163 pte = clear_pte_bit(pte, __pgprot(PTE_RDONLY));
164
165 return pte;
158} 166}
159 167
160static inline pte_t pte_mkold(pte_t pte) 168static inline pte_t pte_mkold(pte_t pte)
@@ -641,28 +649,23 @@ static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
641#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 649#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
642 650
643/* 651/*
644 * ptep_set_wrprotect - mark read-only while preserving the hardware update of 652 * ptep_set_wrprotect - mark read-only while trasferring potential hardware
645 * the Access Flag. 653 * dirty status (PTE_DBM && !PTE_RDONLY) to the software PTE_DIRTY bit.
646 */ 654 */
647#define __HAVE_ARCH_PTEP_SET_WRPROTECT 655#define __HAVE_ARCH_PTEP_SET_WRPROTECT
648static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long address, pte_t *ptep) 656static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long address, pte_t *ptep)
649{ 657{
650 pte_t old_pte, pte; 658 pte_t old_pte, pte;
651 659
652 /*
653 * ptep_set_wrprotect() is only called on CoW mappings which are
654 * private (!VM_SHARED) with the pte either read-only (!PTE_WRITE &&
655 * PTE_RDONLY) or writable and software-dirty (PTE_WRITE &&
656 * !PTE_RDONLY && PTE_DIRTY); see is_cow_mapping() and
657 * protection_map[]. There is no race with the hardware update of the
658 * dirty state: clearing of PTE_RDONLY when PTE_WRITE (a.k.a. PTE_DBM)
659 * is set.
660 */
661 VM_WARN_ONCE(pte_write(*ptep) && !pte_dirty(*ptep),
662 "%s: potential race with hardware DBM", __func__);
663 pte = READ_ONCE(*ptep); 660 pte = READ_ONCE(*ptep);
664 do { 661 do {
665 old_pte = pte; 662 old_pte = pte;
663 /*
664 * If hardware-dirty (PTE_WRITE/DBM bit set and PTE_RDONLY
665 * clear), set the PTE_DIRTY bit.
666 */
667 if (pte_hw_dirty(pte))
668 pte = pte_mkdirty(pte);
666 pte = pte_wrprotect(pte); 669 pte = pte_wrprotect(pte);
667 pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep), 670 pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
668 pte_val(old_pte), pte_val(pte)); 671 pte_val(old_pte), pte_val(pte));