* [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM
@ 2017-08-18 17:25 Catalin Marinas
2017-08-18 17:25 ` [PATCH v2 1/5] arm64: Convert pte handling from inline asm to using (cmp)xchg Catalin Marinas
` (5 more replies)
0 siblings, 6 replies; 8+ messages in thread
From: Catalin Marinas @ 2017-08-18 17:25 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
That's the second version of the pte accessors reworking series (first
version at [1]).
Changes since v1:
- READ_ONCE moved out of the cmpxchg loops since cmpxchg already returns
the old value (probably more efficient with LSE atomics)
- The first patch from the first series has been merged upstream (cc
stable)
- Kconfig entry kept just to disable the hardware feature but the code
paths all assume hardware AF/DBM
- PAGE_COPY(_EXEC) removed as they just duplicate PAGE_READONLY(_EXEC)
I kept the review-by tags in place as the logic is unchanged but feel
free to look at the patches again.
Thanks.
[1] http://lkml.kernel.org/r/20170725135308.18173-1-catalin.marinas at arm.com
Catalin Marinas (5):
arm64: Convert pte handling from inline asm to using (cmp)xchg
kvm: arm64: Convert kvm_set_s2pte_readonly() from inline asm to
cmpxchg()
arm64: Move PTE_RDONLY bit handling out of set_pte_at()
arm64: Ignore hardware dirty bit updates in ptep_set_wrprotect()
arm64: Remove the !CONFIG_ARM64_HW_AFDBM alternative code paths
arch/arm64/include/asm/kvm_mmu.h | 21 +++----
arch/arm64/include/asm/pgtable-prot.h | 18 +++---
arch/arm64/include/asm/pgtable.h | 103 ++++++++++++++--------------------
arch/arm64/kernel/hibernate.c | 4 +-
arch/arm64/kvm/hyp/s2-setup.c | 2 +-
arch/arm64/mm/fault.c | 30 ++++------
6 files changed, 72 insertions(+), 106 deletions(-)
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 1/5] arm64: Convert pte handling from inline asm to using (cmp)xchg
2017-08-18 17:25 [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Catalin Marinas
@ 2017-08-18 17:25 ` Catalin Marinas
2017-08-18 17:25 ` [PATCH v2 2/5] kvm: arm64: Convert kvm_set_s2pte_readonly() from inline asm to cmpxchg() Catalin Marinas
` (4 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Catalin Marinas @ 2017-08-18 17:25 UTC (permalink / raw)
To: linux-arm-kernel
With the support for hardware updates of the access and dirty states,
the following pte handling functions had to be implemented using
exclusives: __ptep_test_and_clear_young(), ptep_get_and_clear(),
ptep_set_wrprotect() and ptep_set_access_flags(). To take advantage of
the LSE atomic instructions and also make the code cleaner, convert
these pte functions to use the more generic cmpxchg()/xchg().
Cc: Will Deacon <will.deacon@arm.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: Steve Capper <steve.capper@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
---
arch/arm64/include/asm/pgtable.h | 71 +++++++++++++++++++---------------------
arch/arm64/mm/fault.c | 24 +++++++-------
2 files changed, 44 insertions(+), 51 deletions(-)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 6eae342ced6b..9127688ae775 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -39,6 +39,7 @@
#ifndef __ASSEMBLY__
+#include <asm/cmpxchg.h>
#include <asm/fixmap.h>
#include <linux/mmdebug.h>
@@ -173,6 +174,11 @@ static inline pte_t pte_clear_rdonly(pte_t pte)
return clear_pte_bit(pte, __pgprot(PTE_RDONLY));
}
+static inline pte_t pte_set_rdonly(pte_t pte)
+{
+ return set_pte_bit(pte, __pgprot(PTE_RDONLY));
+}
+
static inline pte_t pte_mkpresent(pte_t pte)
{
return set_pte_bit(pte, __pgprot(PTE_VALID));
@@ -593,20 +599,17 @@ static inline int pmdp_set_access_flags(struct vm_area_struct *vma,
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
static inline int __ptep_test_and_clear_young(pte_t *ptep)
{
- pteval_t pteval;
- unsigned int tmp, res;
+ pte_t old_pte, pte;
- asm volatile("// __ptep_test_and_clear_young\n"
- " prfm pstl1strm, %2\n"
- "1: ldxr %0, %2\n"
- " ubfx %w3, %w0, %5, #1 // extract PTE_AF (young)\n"
- " and %0, %0, %4 // clear PTE_AF\n"
- " stxr %w1, %0, %2\n"
- " cbnz %w1, 1b\n"
- : "=&r" (pteval), "=&r" (tmp), "+Q" (pte_val(*ptep)), "=&r" (res)
- : "L" (~PTE_AF), "I" (ilog2(PTE_AF)));
+ pte = READ_ONCE(*ptep);
+ do {
+ old_pte = pte;
+ pte = pte_mkold(pte);
+ pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
+ pte_val(old_pte), pte_val(pte));
+ } while (pte_val(pte) != pte_val(old_pte));
- return res;
+ return pte_young(pte);
}
static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
@@ -630,17 +633,7 @@ static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
unsigned long address, pte_t *ptep)
{
- pteval_t old_pteval;
- unsigned int tmp;
-
- asm volatile("// ptep_get_and_clear\n"
- " prfm pstl1strm, %2\n"
- "1: ldxr %0, %2\n"
- " stxr %w1, xzr, %2\n"
- " cbnz %w1, 1b\n"
- : "=&r" (old_pteval), "=&r" (tmp), "+Q" (pte_val(*ptep)));
-
- return __pte(old_pteval);
+ return __pte(xchg_relaxed(&pte_val(*ptep), 0));
}
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
@@ -659,21 +652,23 @@ static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
#define __HAVE_ARCH_PTEP_SET_WRPROTECT
static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long address, pte_t *ptep)
{
- pteval_t pteval;
- unsigned long tmp;
-
- asm volatile("// ptep_set_wrprotect\n"
- " prfm pstl1strm, %2\n"
- "1: ldxr %0, %2\n"
- " tst %0, %4 // check for hw dirty (!PTE_RDONLY)\n"
- " csel %1, %3, xzr, eq // set PTE_DIRTY|PTE_RDONLY if dirty\n"
- " orr %0, %0, %1 // if !dirty, PTE_RDONLY is already set\n"
- " and %0, %0, %5 // clear PTE_WRITE/PTE_DBM\n"
- " stxr %w1, %0, %2\n"
- " cbnz %w1, 1b\n"
- : "=&r" (pteval), "=&r" (tmp), "+Q" (pte_val(*ptep))
- : "r" (PTE_DIRTY|PTE_RDONLY), "L" (PTE_RDONLY), "L" (~PTE_WRITE)
- : "cc");
+ pte_t old_pte, pte;
+
+ pte = READ_ONCE(*ptep);
+ do {
+ old_pte = pte;
+ /*
+ * If hardware-dirty (PTE_WRITE/DBM bit set and PTE_RDONLY
+ * clear), set the PTE_DIRTY and PTE_RDONLY bits.
+ */
+ if (pte_hw_dirty(pte)) {
+ pte = pte_mkdirty(pte);
+ pte = pte_set_rdonly(pte);
+ }
+ pte = pte_wrprotect(pte);
+ pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
+ pte_val(old_pte), pte_val(pte));
+ } while (pte_val(pte) != pte_val(old_pte));
}
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 2509e4fe6992..9b861986548e 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -34,6 +34,7 @@
#include <linux/hugetlb.h>
#include <asm/bug.h>
+#include <asm/cmpxchg.h>
#include <asm/cpufeature.h>
#include <asm/exception.h>
#include <asm/debug-monitors.h>
@@ -154,8 +155,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep,
pte_t entry, int dirty)
{
- pteval_t old_pteval;
- unsigned int tmp;
+ pteval_t old_pteval, pteval;
if (pte_same(*ptep, entry))
return 0;
@@ -165,7 +165,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
/* set PTE_RDONLY if actual read-only or clean PTE */
if (!pte_write(entry) || !pte_sw_dirty(entry))
- pte_val(entry) |= PTE_RDONLY;
+ entry = pte_set_rdonly(entry);
/*
* Setting the flags must be done atomically to avoid racing with the
@@ -174,16 +174,14 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
* (calculated as: a & b == ~(~a | ~b)).
*/
pte_val(entry) ^= PTE_RDONLY;
- asm volatile("// ptep_set_access_flags\n"
- " prfm pstl1strm, %2\n"
- "1: ldxr %0, %2\n"
- " eor %0, %0, %3 // negate PTE_RDONLY in *ptep\n"
- " orr %0, %0, %4 // set flags\n"
- " eor %0, %0, %3 // negate final PTE_RDONLY\n"
- " stxr %w1, %0, %2\n"
- " cbnz %w1, 1b\n"
- : "=&r" (old_pteval), "=&r" (tmp), "+Q" (pte_val(*ptep))
- : "L" (PTE_RDONLY), "r" (pte_val(entry)));
+ pteval = READ_ONCE(pte_val(*ptep));
+ do {
+ old_pteval = pteval;
+ pteval ^= PTE_RDONLY;
+ pteval |= pte_val(entry);
+ pteval ^= PTE_RDONLY;
+ pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval, pteval);
+ } while (pteval != old_pteval);
flush_tlb_fix_spurious_fault(vma, address);
return 1;
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 2/5] kvm: arm64: Convert kvm_set_s2pte_readonly() from inline asm to cmpxchg()
2017-08-18 17:25 [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Catalin Marinas
2017-08-18 17:25 ` [PATCH v2 1/5] arm64: Convert pte handling from inline asm to using (cmp)xchg Catalin Marinas
@ 2017-08-18 17:25 ` Catalin Marinas
2017-08-18 17:26 ` [PATCH v2 3/5] arm64: Move PTE_RDONLY bit handling out of set_pte_at() Catalin Marinas
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Catalin Marinas @ 2017-08-18 17:25 UTC (permalink / raw)
To: linux-arm-kernel
To take advantage of the LSE atomic instructions and also make the code
cleaner, convert the kvm_set_s2pte_readonly() function to use the more
generic cmpxchg().
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
---
arch/arm64/include/asm/kvm_mmu.h | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index a89cc22abadc..672c8684d5c2 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -175,18 +175,15 @@ static inline pmd_t kvm_s2pmd_mkwrite(pmd_t pmd)
static inline void kvm_set_s2pte_readonly(pte_t *pte)
{
- pteval_t pteval;
- unsigned long tmp;
-
- asm volatile("// kvm_set_s2pte_readonly\n"
- " prfm pstl1strm, %2\n"
- "1: ldxr %0, %2\n"
- " and %0, %0, %3 // clear PTE_S2_RDWR\n"
- " orr %0, %0, %4 // set PTE_S2_RDONLY\n"
- " stxr %w1, %0, %2\n"
- " cbnz %w1, 1b\n"
- : "=&r" (pteval), "=&r" (tmp), "+Q" (pte_val(*pte))
- : "L" (~PTE_S2_RDWR), "L" (PTE_S2_RDONLY));
+ pteval_t old_pteval, pteval;
+
+ pteval = READ_ONCE(pte_val(*pte));
+ do {
+ old_pteval = pteval;
+ pteval &= ~PTE_S2_RDWR;
+ pteval |= PTE_S2_RDONLY;
+ pteval = cmpxchg_relaxed(&pte_val(*pte), old_pteval, pteval);
+ } while (pteval != old_pteval);
}
static inline bool kvm_s2pte_readonly(pte_t *pte)
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 3/5] arm64: Move PTE_RDONLY bit handling out of set_pte_at()
2017-08-18 17:25 [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Catalin Marinas
2017-08-18 17:25 ` [PATCH v2 1/5] arm64: Convert pte handling from inline asm to using (cmp)xchg Catalin Marinas
2017-08-18 17:25 ` [PATCH v2 2/5] kvm: arm64: Convert kvm_set_s2pte_readonly() from inline asm to cmpxchg() Catalin Marinas
@ 2017-08-18 17:26 ` Catalin Marinas
2017-08-18 17:26 ` [PATCH v2 4/5] arm64: Ignore hardware dirty bit updates in ptep_set_wrprotect() Catalin Marinas
` (2 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Catalin Marinas @ 2017-08-18 17:26 UTC (permalink / raw)
To: linux-arm-kernel
Currently PTE_RDONLY is treated as a hardware only bit and not handled
by the pte_mkwrite(), pte_wrprotect() or the user PAGE_* definitions.
The set_pte_at() function is responsible for setting this bit based on
the write permission or dirty state. This patch moves the PTE_RDONLY
handling out of set_pte_at into the pte_mkwrite()/pte_wrprotect()
functions. The PAGE_* definitions to need to be updated to explicitly
include PTE_RDONLY when !PTE_WRITE.
The patch also removes the redundant PAGE_COPY(_EXEC) definitions as
they are identical to the corresponding PAGE_READONLY(_EXEC).
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
---
arch/arm64/include/asm/pgtable-prot.h | 18 ++++++++----------
arch/arm64/include/asm/pgtable.h | 34 ++++++++++------------------------
arch/arm64/kernel/hibernate.c | 4 ++--
arch/arm64/mm/fault.c | 6 +-----
4 files changed, 21 insertions(+), 41 deletions(-)
diff --git a/arch/arm64/include/asm/pgtable-prot.h b/arch/arm64/include/asm/pgtable-prot.h
index 2142c7726e76..0a5635fb0ef9 100644
--- a/arch/arm64/include/asm/pgtable-prot.h
+++ b/arch/arm64/include/asm/pgtable-prot.h
@@ -63,23 +63,21 @@
#define PAGE_S2 __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY)
#define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_UXN)
-#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_PXN | PTE_UXN)
+#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_RDONLY | PTE_PXN | PTE_UXN)
#define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)
#define PAGE_SHARED_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_WRITE)
-#define PAGE_COPY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN)
-#define PAGE_COPY_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN)
-#define PAGE_READONLY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN)
-#define PAGE_READONLY_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN)
-#define PAGE_EXECONLY __pgprot(_PAGE_DEFAULT | PTE_NG | PTE_PXN)
+#define PAGE_READONLY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
+#define PAGE_READONLY_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN)
+#define PAGE_EXECONLY __pgprot(_PAGE_DEFAULT | PTE_RDONLY | PTE_NG | PTE_PXN)
#define __P000 PAGE_NONE
#define __P001 PAGE_READONLY
-#define __P010 PAGE_COPY
-#define __P011 PAGE_COPY
+#define __P010 PAGE_READONLY
+#define __P011 PAGE_READONLY
#define __P100 PAGE_EXECONLY
#define __P101 PAGE_READONLY_EXEC
-#define __P110 PAGE_COPY_EXEC
-#define __P111 PAGE_COPY_EXEC
+#define __P110 PAGE_READONLY_EXEC
+#define __P111 PAGE_READONLY_EXEC
#define __S000 PAGE_NONE
#define __S001 PAGE_READONLY
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 9127688ae775..a04bfb869a80 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -125,12 +125,16 @@ static inline pte_t set_pte_bit(pte_t pte, pgprot_t prot)
static inline pte_t pte_wrprotect(pte_t pte)
{
- return clear_pte_bit(pte, __pgprot(PTE_WRITE));
+ 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)
{
- return set_pte_bit(pte, __pgprot(PTE_WRITE));
+ pte = set_pte_bit(pte, __pgprot(PTE_WRITE));
+ pte = clear_pte_bit(pte, __pgprot(PTE_RDONLY));
+ return pte;
}
static inline pte_t pte_mkclean(pte_t pte)
@@ -169,16 +173,6 @@ static inline pte_t pte_mknoncont(pte_t pte)
return clear_pte_bit(pte, __pgprot(PTE_CONT));
}
-static inline pte_t pte_clear_rdonly(pte_t pte)
-{
- return clear_pte_bit(pte, __pgprot(PTE_RDONLY));
-}
-
-static inline pte_t pte_set_rdonly(pte_t pte)
-{
- return set_pte_bit(pte, __pgprot(PTE_RDONLY));
-}
-
static inline pte_t pte_mkpresent(pte_t pte)
{
return set_pte_bit(pte, __pgprot(PTE_VALID));
@@ -226,14 +220,8 @@ extern void __sync_icache_dcache(pte_t pteval, unsigned long addr);
static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pte)
{
- if (pte_present(pte)) {
- if (pte_sw_dirty(pte) && pte_write(pte))
- pte_val(pte) &= ~PTE_RDONLY;
- else
- pte_val(pte) |= PTE_RDONLY;
- if (pte_user_exec(pte) && !pte_special(pte))
- __sync_icache_dcache(pte, addr);
- }
+ if (pte_present(pte) && pte_user_exec(pte) && !pte_special(pte))
+ __sync_icache_dcache(pte, addr);
/*
* If the existing pte is valid, check for potential race with
@@ -659,12 +647,10 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres
old_pte = pte;
/*
* If hardware-dirty (PTE_WRITE/DBM bit set and PTE_RDONLY
- * clear), set the PTE_DIRTY and PTE_RDONLY bits.
+ * clear), set the PTE_DIRTY bit.
*/
- if (pte_hw_dirty(pte)) {
+ if (pte_hw_dirty(pte))
pte = pte_mkdirty(pte);
- pte = pte_set_rdonly(pte);
- }
pte = pte_wrprotect(pte);
pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep),
pte_val(old_pte), pte_val(pte));
diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
index a44e13942d30..095d3c170f5d 100644
--- a/arch/arm64/kernel/hibernate.c
+++ b/arch/arm64/kernel/hibernate.c
@@ -330,7 +330,7 @@ static void _copy_pte(pte_t *dst_pte, pte_t *src_pte, unsigned long addr)
* read only (code, rodata). Clear the RDONLY bit from
* the temporary mappings we use during restore.
*/
- set_pte(dst_pte, pte_clear_rdonly(pte));
+ set_pte(dst_pte, pte_mkwrite(pte));
} else if (debug_pagealloc_enabled() && !pte_none(pte)) {
/*
* debug_pagealloc will removed the PTE_VALID bit if
@@ -343,7 +343,7 @@ static void _copy_pte(pte_t *dst_pte, pte_t *src_pte, unsigned long addr)
*/
BUG_ON(!pfn_valid(pte_pfn(pte)));
- set_pte(dst_pte, pte_mkpresent(pte_clear_rdonly(pte)));
+ set_pte(dst_pte, pte_mkpresent(pte_mkwrite(pte)));
}
}
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 9b861986548e..c3eebb4afd6c 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -161,11 +161,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
return 0;
/* only preserve the access flags and write permission */
- pte_val(entry) &= PTE_AF | PTE_WRITE | PTE_DIRTY;
-
- /* set PTE_RDONLY if actual read-only or clean PTE */
- if (!pte_write(entry) || !pte_sw_dirty(entry))
- entry = pte_set_rdonly(entry);
+ pte_val(entry) &= PTE_RDONLY | PTE_AF | PTE_WRITE | PTE_DIRTY;
/*
* Setting the flags must be done atomically to avoid racing with the
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 4/5] arm64: Ignore hardware dirty bit updates in ptep_set_wrprotect()
2017-08-18 17:25 [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Catalin Marinas
` (2 preceding siblings ...)
2017-08-18 17:26 ` [PATCH v2 3/5] arm64: Move PTE_RDONLY bit handling out of set_pte_at() Catalin Marinas
@ 2017-08-18 17:26 ` Catalin Marinas
2017-08-18 17:26 ` [PATCH v2 5/5] arm64: Remove the !CONFIG_ARM64_HW_AFDBM alternative code paths Catalin Marinas
2017-08-18 17:29 ` [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Will Deacon
5 siblings, 0 replies; 8+ messages in thread
From: Catalin Marinas @ 2017-08-18 17:26 UTC (permalink / raw)
To: linux-arm-kernel
ptep_set_wrprotect() is only called on CoW mappings which are private
(!VM_SHARED) with the pte either read-only (!PTE_WRITE && PTE_RDONLY) or
writable and software-dirty (PTE_WRITE && !PTE_RDONLY && PTE_DIRTY).
There is no race with the hardware update of the dirty state: clearing
of PTE_RDONLY when PTE_WRITE (a.k.a. PTE_DBM) is set. This patch removes
the code setting the software PTE_DIRTY bit in ptep_set_wrprotect() as
superfluous. A VM_WARN_ONCE is introduced in case the above logic is
wrong or the core mm code changes its use of ptep_set_wrprotect().
Cc: Will Deacon <will.deacon@arm.com>
Acked-by: Steve Capper <steve.capper@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
---
arch/arm64/include/asm/pgtable.h | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index a04bfb869a80..0117cbcd62d4 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -634,23 +634,28 @@ static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
/*
- * ptep_set_wrprotect - mark read-only while trasferring potential hardware
- * dirty status (PTE_DBM && !PTE_RDONLY) to the software PTE_DIRTY bit.
+ * ptep_set_wrprotect - mark read-only while preserving the hardware update of
+ * the Access Flag.
*/
#define __HAVE_ARCH_PTEP_SET_WRPROTECT
static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long address, pte_t *ptep)
{
pte_t old_pte, pte;
+ /*
+ * ptep_set_wrprotect() is only called on CoW mappings which are
+ * private (!VM_SHARED) with the pte either read-only (!PTE_WRITE &&
+ * PTE_RDONLY) or writable and software-dirty (PTE_WRITE &&
+ * !PTE_RDONLY && PTE_DIRTY); see is_cow_mapping() and
+ * protection_map[]. There is no race with the hardware update of the
+ * dirty state: clearing of PTE_RDONLY when PTE_WRITE (a.k.a. PTE_DBM)
+ * is set.
+ */
+ VM_WARN_ONCE(pte_write(*ptep) && !pte_dirty(*ptep),
+ "%s: potential race with hardware DBM", __func__);
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));
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 5/5] arm64: Remove the !CONFIG_ARM64_HW_AFDBM alternative code paths
2017-08-18 17:25 [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Catalin Marinas
` (3 preceding siblings ...)
2017-08-18 17:26 ` [PATCH v2 4/5] arm64: Ignore hardware dirty bit updates in ptep_set_wrprotect() Catalin Marinas
@ 2017-08-18 17:26 ` Catalin Marinas
2017-08-18 17:29 ` [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Will Deacon
5 siblings, 0 replies; 8+ messages in thread
From: Catalin Marinas @ 2017-08-18 17:26 UTC (permalink / raw)
To: linux-arm-kernel
Since the pte handling for hardware AF/DBM works even when the hardware
feature is not present, make the pte accessors implementation permanent
and remove the corresponding #ifdefs. The Kconfig option is kept as it
can still be used to disable the feature at the hardware level.
Cc: Will Deacon <will.deacon@arm.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
---
arch/arm64/include/asm/pgtable.h | 9 +--------
arch/arm64/kvm/hyp/s2-setup.c | 2 +-
arch/arm64/mm/fault.c | 2 --
3 files changed, 2 insertions(+), 11 deletions(-)
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 0117cbcd62d4..bc4e92337d16 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -85,11 +85,7 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
(__boundary - 1 < (end) - 1) ? __boundary : (end); \
})
-#ifdef CONFIG_ARM64_HW_AFDBM
#define pte_hw_dirty(pte) (pte_write(pte) && !(pte_val(pte) & PTE_RDONLY))
-#else
-#define pte_hw_dirty(pte) (0)
-#endif
#define pte_sw_dirty(pte) (!!(pte_val(pte) & PTE_DIRTY))
#define pte_dirty(pte) (pte_sw_dirty(pte) || pte_hw_dirty(pte))
@@ -228,8 +224,7 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
* hardware updates of the pte (ptep_set_access_flags safely changes
* valid ptes without going through an invalid entry).
*/
- if (IS_ENABLED(CONFIG_ARM64_HW_AFDBM) &&
- pte_valid(*ptep) && pte_valid(pte)) {
+ if (pte_valid(*ptep) && pte_valid(pte)) {
VM_WARN_ONCE(!pte_young(pte),
"%s: racy access flag clearing: 0x%016llx -> 0x%016llx",
__func__, pte_val(*ptep), pte_val(pte));
@@ -565,7 +560,6 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
return pte_pmd(pte_modify(pmd_pte(pmd), newprot));
}
-#ifdef CONFIG_ARM64_HW_AFDBM
#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
extern int ptep_set_access_flags(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep,
@@ -670,7 +664,6 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm,
ptep_set_wrprotect(mm, address, (pte_t *)pmdp);
}
#endif
-#endif /* CONFIG_ARM64_HW_AFDBM */
extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
extern pgd_t idmap_pg_dir[PTRS_PER_PGD];
diff --git a/arch/arm64/kvm/hyp/s2-setup.c b/arch/arm64/kvm/hyp/s2-setup.c
index b81f4091c909..a81f5e10fc8c 100644
--- a/arch/arm64/kvm/hyp/s2-setup.c
+++ b/arch/arm64/kvm/hyp/s2-setup.c
@@ -70,7 +70,7 @@ u32 __hyp_text __init_stage2_translation(void)
* Management in ID_AA64MMFR1_EL1 and enable the feature in VTCR_EL2.
*/
tmp = (read_sysreg(id_aa64mmfr1_el1) >> ID_AA64MMFR1_HADBS_SHIFT) & 0xf;
- if (IS_ENABLED(CONFIG_ARM64_HW_AFDBM) && tmp)
+ if (tmp)
val |= VTCR_EL2_HA;
/*
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index c3eebb4afd6c..4428b783147b 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -140,7 +140,6 @@ void show_pte(unsigned long addr)
pr_cont("\n");
}
-#ifdef CONFIG_ARM64_HW_AFDBM
/*
* This function sets the access flags (dirty, accessed), as well as write
* permission, and only to a more permissive setting.
@@ -182,7 +181,6 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
flush_tlb_fix_spurious_fault(vma, address);
return 1;
}
-#endif
static bool is_el1_instruction_abort(unsigned int esr)
{
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM
2017-08-18 17:25 [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Catalin Marinas
` (4 preceding siblings ...)
2017-08-18 17:26 ` [PATCH v2 5/5] arm64: Remove the !CONFIG_ARM64_HW_AFDBM alternative code paths Catalin Marinas
@ 2017-08-18 17:29 ` Will Deacon
2017-08-21 10:41 ` Catalin Marinas
5 siblings, 1 reply; 8+ messages in thread
From: Will Deacon @ 2017-08-18 17:29 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Aug 18, 2017 at 06:25:57PM +0100, Catalin Marinas wrote:
> That's the second version of the pte accessors reworking series (first
> version at [1]).
>
> Changes since v1:
>
> - READ_ONCE moved out of the cmpxchg loops since cmpxchg already returns
> the old value (probably more efficient with LSE atomics)
>
> - The first patch from the first series has been merged upstream (cc
> stable)
>
> - Kconfig entry kept just to disable the hardware feature but the code
> paths all assume hardware AF/DBM
>
> - PAGE_COPY(_EXEC) removed as they just duplicate PAGE_READONLY(_EXEC)
>
> I kept the review-by tags in place as the logic is unchanged but feel
> free to look at the patches again.
Thanks, this looks good to me now:
Reviewed-by: Will Deacon <will.deacon@arm.com>
Will
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM
2017-08-18 17:29 ` [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Will Deacon
@ 2017-08-21 10:41 ` Catalin Marinas
0 siblings, 0 replies; 8+ messages in thread
From: Catalin Marinas @ 2017-08-21 10:41 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Aug 18, 2017 at 06:29:36PM +0100, Will Deacon wrote:
> On Fri, Aug 18, 2017 at 06:25:57PM +0100, Catalin Marinas wrote:
> > That's the second version of the pte accessors reworking series (first
> > version at [1]).
> >
> > Changes since v1:
> >
> > - READ_ONCE moved out of the cmpxchg loops since cmpxchg already returns
> > the old value (probably more efficient with LSE atomics)
> >
> > - The first patch from the first series has been merged upstream (cc
> > stable)
> >
> > - Kconfig entry kept just to disable the hardware feature but the code
> > paths all assume hardware AF/DBM
> >
> > - PAGE_COPY(_EXEC) removed as they just duplicate PAGE_READONLY(_EXEC)
> >
> > I kept the review-by tags in place as the logic is unchanged but feel
> > free to look at the patches again.
>
> Thanks, this looks good to me now:
>
> Reviewed-by: Will Deacon <will.deacon@arm.com>
Thanks. I merged them into for-next/core.
--
Catalin
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2017-08-21 10:41 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-08-18 17:25 [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Catalin Marinas
2017-08-18 17:25 ` [PATCH v2 1/5] arm64: Convert pte handling from inline asm to using (cmp)xchg Catalin Marinas
2017-08-18 17:25 ` [PATCH v2 2/5] kvm: arm64: Convert kvm_set_s2pte_readonly() from inline asm to cmpxchg() Catalin Marinas
2017-08-18 17:26 ` [PATCH v2 3/5] arm64: Move PTE_RDONLY bit handling out of set_pte_at() Catalin Marinas
2017-08-18 17:26 ` [PATCH v2 4/5] arm64: Ignore hardware dirty bit updates in ptep_set_wrprotect() Catalin Marinas
2017-08-18 17:26 ` [PATCH v2 5/5] arm64: Remove the !CONFIG_ARM64_HW_AFDBM alternative code paths Catalin Marinas
2017-08-18 17:29 ` [PATCH v2 0/5] Rework the pte handling for hardware AF/DBM Will Deacon
2017-08-21 10:41 ` Catalin Marinas
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).