diff options
-rw-r--r-- | arch/arm64/include/asm/pgtable.h | 33 |
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 | ||
150 | static inline pte_t pte_mkclean(pte_t pte) | 150 | static 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 | ||
155 | static inline pte_t pte_mkdirty(pte_t pte) | 158 | static 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 | ||
160 | static inline pte_t pte_mkold(pte_t pte) | 168 | static 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 |
648 | static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long address, pte_t *ptep) | 656 | static 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)); |