diff options
author | Will Deacon <will.deacon@arm.com> | 2017-12-12 05:48:54 -0500 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2017-12-12 06:42:24 -0500 |
commit | 86c9e8126e9fbcbf06c36e285168b880369a537c (patch) | |
tree | 91889a0eaf4e732fa83a17f9d9e55c192c25574a | |
parent | 8781bcbc5e69d7da69e84c7044ca0284848d5d01 (diff) |
arm64: mm: Fix false positives in set_pte_at access/dirty race detection
Jiankang reports that our race detection in set_pte_at is firing when
copying the page tables in dup_mmap as a result of a fork(). In this
situation, the page table isn't actually live and so there is no way
that we can race with a concurrent update from the hardware page table
walker.
This patch reworks the race detection so that we require either the
mm to match the current active_mm (i.e. currently installed in our TTBR0)
or the mm_users count to be greater than 1, implying that the page table
could be live in another CPU. The mm_users check might still be racy,
but we'll avoid false positives and it's not realistic to validate that
all the necessary locks are held as part of this assertion.
Cc: Yisheng Xie <xieyisheng1@huawei.com>
Reported-by: Jiankang Chen <chenjiankang1@huawei.com>
Tested-by: Jiankang Chen <chenjiankang1@huawei.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r-- | arch/arm64/include/asm/pgtable.h | 8 |
1 files changed, 4 insertions, 4 deletions
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 3ff03a755c32..bdcc7f1c9d06 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h | |||
@@ -42,6 +42,8 @@ | |||
42 | #include <asm/cmpxchg.h> | 42 | #include <asm/cmpxchg.h> |
43 | #include <asm/fixmap.h> | 43 | #include <asm/fixmap.h> |
44 | #include <linux/mmdebug.h> | 44 | #include <linux/mmdebug.h> |
45 | #include <linux/mm_types.h> | ||
46 | #include <linux/sched.h> | ||
45 | 47 | ||
46 | extern void __pte_error(const char *file, int line, unsigned long val); | 48 | extern void __pte_error(const char *file, int line, unsigned long val); |
47 | extern void __pmd_error(const char *file, int line, unsigned long val); | 49 | extern void __pmd_error(const char *file, int line, unsigned long val); |
@@ -215,9 +217,6 @@ static inline void set_pte(pte_t *ptep, pte_t pte) | |||
215 | } | 217 | } |
216 | } | 218 | } |
217 | 219 | ||
218 | struct mm_struct; | ||
219 | struct vm_area_struct; | ||
220 | |||
221 | extern void __sync_icache_dcache(pte_t pteval, unsigned long addr); | 220 | extern void __sync_icache_dcache(pte_t pteval, unsigned long addr); |
222 | 221 | ||
223 | /* | 222 | /* |
@@ -246,7 +245,8 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, | |||
246 | * hardware updates of the pte (ptep_set_access_flags safely changes | 245 | * hardware updates of the pte (ptep_set_access_flags safely changes |
247 | * valid ptes without going through an invalid entry). | 246 | * valid ptes without going through an invalid entry). |
248 | */ | 247 | */ |
249 | if (pte_valid(*ptep) && pte_valid(pte)) { | 248 | if (IS_ENABLED(CONFIG_DEBUG_VM) && pte_valid(*ptep) && pte_valid(pte) && |
249 | (mm == current->active_mm || atomic_read(&mm->mm_users) > 1)) { | ||
250 | VM_WARN_ONCE(!pte_young(pte), | 250 | VM_WARN_ONCE(!pte_young(pte), |
251 | "%s: racy access flag clearing: 0x%016llx -> 0x%016llx", | 251 | "%s: racy access flag clearing: 0x%016llx -> 0x%016llx", |
252 | __func__, pte_val(*ptep), pte_val(pte)); | 252 | __func__, pte_val(*ptep), pte_val(pte)); |