diff mbox series

arm64: mm: avoid redundant READ_ONCE(*ptep)

Message ID 20190610124107.16497-1-mark.rutland@arm.com (mailing list archive)
State Mainlined
Commit 9b604722059039a1a3ff69fb8dfd024264046024
Headers show
Series arm64: mm: avoid redundant READ_ONCE(*ptep) | expand

Commit Message

Mark Rutland June 10, 2019, 12:41 p.m. UTC
In set_pte_at(), we read the old pte value so that it can be passed into
checks for racy hw updates. These checks are only performed for
CONFIG_DEBUG_VM, and the value is not used otherwise.

Since we read the pte value with READ_ONCE(), the compiler cannot elide
the redundant read for !CONFIG_DEBUG_VM kernels.

Let's ameliorate matters by moving the read and the checks into a
helper, __check_racy_pte_update(), which only performs the read when the
value will be used. This also allows us to reformat the conditions for
clarity.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
---
 arch/arm64/include/asm/pgtable.h | 47 +++++++++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 17 deletions(-)

Comments

Will Deacon June 10, 2019, 12:51 p.m. UTC | #1
On Mon, Jun 10, 2019 at 01:41:07PM +0100, Mark Rutland wrote:
> In set_pte_at(), we read the old pte value so that it can be passed into
> checks for racy hw updates. These checks are only performed for
> CONFIG_DEBUG_VM, and the value is not used otherwise.
> 
> Since we read the pte value with READ_ONCE(), the compiler cannot elide
> the redundant read for !CONFIG_DEBUG_VM kernels.
> 
> Let's ameliorate matters by moving the read and the checks into a
> helper, __check_racy_pte_update(), which only performs the read when the
> value will be used. This also allows us to reformat the conditions for
> clarity.
> 
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> ---
>  arch/arm64/include/asm/pgtable.h | 47 +++++++++++++++++++++++++---------------
>  1 file changed, 30 insertions(+), 17 deletions(-)

Acked-by: Will Deacon <will.deacon@arm.com>

Will
Catalin Marinas June 10, 2019, 12:55 p.m. UTC | #2
On Mon, Jun 10, 2019 at 01:41:07PM +0100, Mark Rutland wrote:
> In set_pte_at(), we read the old pte value so that it can be passed into
> checks for racy hw updates. These checks are only performed for
> CONFIG_DEBUG_VM, and the value is not used otherwise.
> 
> Since we read the pte value with READ_ONCE(), the compiler cannot elide
> the redundant read for !CONFIG_DEBUG_VM kernels.
> 
> Let's ameliorate matters by moving the read and the checks into a
> helper, __check_racy_pte_update(), which only performs the read when the
> value will be used. This also allows us to reformat the conditions for
> clarity.
> 
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>

Queued for 5.3. Thanks.
diff mbox series

Patch

diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 2c41b04708fe..352048e7897e 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -246,29 +246,42 @@  extern void __sync_icache_dcache(pte_t pteval);
  *
  *   PTE_DIRTY || (PTE_WRITE && !PTE_RDONLY)
  */
-static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
-			      pte_t *ptep, pte_t pte)
+
+static inline void __check_racy_pte_update(struct mm_struct *mm, pte_t *ptep,
+					   pte_t pte)
 {
 	pte_t old_pte;
 
-	if (pte_present(pte) && pte_user_exec(pte) && !pte_special(pte))
-		__sync_icache_dcache(pte);
+	if (!IS_ENABLED(CONFIG_DEBUG_VM))
+		return;
+
+	old_pte = READ_ONCE(*ptep);
+
+	if (!pte_valid(old_pte) || !pte_valid(pte))
+		return;
+	if (mm != current->active_mm && atomic_read(&mm->mm_users) <= 1)
+		return;
 
 	/*
-	 * If the existing pte is valid, check for potential race with
-	 * hardware updates of the pte (ptep_set_access_flags safely changes
-	 * valid ptes without going through an invalid entry).
+	 * Check for potential race with hardware updates of the pte
+	 * (ptep_set_access_flags safely changes valid ptes without going
+	 * through an invalid entry).
 	 */
-	old_pte = READ_ONCE(*ptep);
-	if (IS_ENABLED(CONFIG_DEBUG_VM) && pte_valid(old_pte) && pte_valid(pte) &&
-	   (mm == current->active_mm || atomic_read(&mm->mm_users) > 1)) {
-		VM_WARN_ONCE(!pte_young(pte),
-			     "%s: racy access flag clearing: 0x%016llx -> 0x%016llx",
-			     __func__, pte_val(old_pte), pte_val(pte));
-		VM_WARN_ONCE(pte_write(old_pte) && !pte_dirty(pte),
-			     "%s: racy dirty state clearing: 0x%016llx -> 0x%016llx",
-			     __func__, pte_val(old_pte), pte_val(pte));
-	}
+	VM_WARN_ONCE(!pte_young(pte),
+		     "%s: racy access flag clearing: 0x%016llx -> 0x%016llx",
+		     __func__, pte_val(old_pte), pte_val(pte));
+	VM_WARN_ONCE(pte_write(old_pte) && !pte_dirty(pte),
+		     "%s: racy dirty state clearing: 0x%016llx -> 0x%016llx",
+		     __func__, pte_val(old_pte), pte_val(pte));
+}
+
+static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
+			      pte_t *ptep, pte_t pte)
+{
+	if (pte_present(pte) && pte_user_exec(pte) && !pte_special(pte))
+		__sync_icache_dcache(pte);
+
+	__check_racy_pte_update(mm, ptep, pte);
 
 	set_pte(ptep, pte);
 }