]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
arm64: pgtable: Ensure dirty bit is preserved across pte_wrprotect()
authorWill Deacon <will@kernel.org>
Fri, 20 Nov 2020 13:57:48 +0000 (13:57 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 2 Dec 2020 07:51:47 +0000 (08:51 +0100)
commit ff1712f953e27f0b0718762ec17d0adb15c9fd0b upstream.

With hardware dirty bit management, calling pte_wrprotect() on a writable,
dirty PTE will lose the dirty state and return a read-only, clean entry.

Move the logic from ptep_set_wrprotect() into pte_wrprotect() to ensure that
the dirty bit is preserved for writable entries, as this is required for
soft-dirty bit management if we enable it in the future.

Cc: <stable@vger.kernel.org>
Fixes: 2f4b829c625e ("arm64: Add support for hardware updates of the access and dirty pte bits")
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/20201120143557.6715-3-will@kernel.org
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/arm64/include/asm/pgtable.h

index 7a556e394b8fb3d42593ef46e4c362a52e98f361..db16919a53e4aebc621635c2d4d8c493cec5926b 100644 (file)
@@ -146,13 +146,6 @@ static inline pte_t set_pte_bit(pte_t pte, pgprot_t prot)
        return pte;
 }
 
-static inline pte_t pte_wrprotect(pte_t pte)
-{
-       pte = clear_pte_bit(pte, __pgprot(PTE_WRITE));
-       pte = set_pte_bit(pte, __pgprot(PTE_RDONLY));
-       return pte;
-}
-
 static inline pte_t pte_mkwrite(pte_t pte)
 {
        pte = set_pte_bit(pte, __pgprot(PTE_WRITE));
@@ -178,6 +171,20 @@ static inline pte_t pte_mkdirty(pte_t pte)
        return pte;
 }
 
+static inline pte_t pte_wrprotect(pte_t pte)
+{
+       /*
+        * If hardware-dirty (PTE_WRITE/DBM bit set and PTE_RDONLY
+        * clear), set the PTE_DIRTY bit.
+        */
+       if (pte_hw_dirty(pte))
+               pte = pte_mkdirty(pte);
+
+       pte = clear_pte_bit(pte, __pgprot(PTE_WRITE));
+       pte = set_pte_bit(pte, __pgprot(PTE_RDONLY));
+       return pte;
+}
+
 static inline pte_t pte_mkold(pte_t pte)
 {
        return clear_pte_bit(pte, __pgprot(PTE_AF));
@@ -799,12 +806,6 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres
        pte = READ_ONCE(*ptep);
        do {
                old_pte = pte;
-               /*
-                * If hardware-dirty (PTE_WRITE/DBM bit set and PTE_RDONLY
-                * clear), set the PTE_DIRTY bit.
-                */
-               if (pte_hw_dirty(pte))
-                       pte = pte_mkdirty(pte);
                pte = pte_wrprotect(pte);
                pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
                                               pte_val(old_pte), pte_val(pte));