| From ff1712f953e27f0b0718762ec17d0adb15c9fd0b Mon Sep 17 00:00:00 2001 |
| From: Will Deacon <will@kernel.org> |
| Date: Fri, 20 Nov 2020 13:57:48 +0000 |
| Subject: arm64: pgtable: Ensure dirty bit is preserved across pte_wrprotect() |
| |
| From: Will Deacon <will@kernel.org> |
| |
| 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 | 27 ++++++++++++++------------- |
| 1 file changed, 14 insertions(+), 13 deletions(-) |
| |
| --- a/arch/arm64/include/asm/pgtable.h |
| +++ b/arch/arm64/include/asm/pgtable.h |
| @@ -136,13 +136,6 @@ static inline pte_t set_pte_bit(pte_t pt |
| 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)); |
| @@ -168,6 +161,20 @@ static inline pte_t pte_mkdirty(pte_t pt |
| 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)); |
| @@ -783,12 +790,6 @@ static inline void ptep_set_wrprotect(st |
| 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)); |