diff options
author | Andrea Arcangeli <aarcange@redhat.com> | 2016-02-26 15:19:28 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-09-11 09:59:59 +0200 |
commit | 09a2499466dc69d1e54e8e879d4591cdd0ca17c8 (patch) | |
tree | 6ded058db8cffa190aecdfbd48f7abdfbd159580 | |
parent | 7cac57a69919afdf3bdda5242afdd535b2d9a2b0 (diff) | |
download | linux-stable-09a2499466dc69d1e54e8e879d4591cdd0ca17c8.tar.gz linux-stable-09a2499466dc69d1e54e8e879d4591cdd0ca17c8.tar.bz2 linux-stable-09a2499466dc69d1e54e8e879d4591cdd0ca17c8.zip |
mm: thp: fix SMP race condition between THP page fault and MADV_DONTNEED
commit ad33bb04b2a6cee6c1f99fabb15cddbf93ff0433 upstream.
pmd_trans_unstable()/pmd_none_or_trans_huge_or_clear_bad() were
introduced to locklessy (but atomically) detect when a pmd is a regular
(stable) pmd or when the pmd is unstable and can infinitely transition
from pmd_none() and pmd_trans_huge() from under us, while only holding
the mmap_sem for reading (for writing not).
While holding the mmap_sem only for reading, MADV_DONTNEED can run from
under us and so before we can assume the pmd to be a regular stable pmd
we need to compare it against pmd_none() and pmd_trans_huge() in an
atomic way, with pmd_trans_unstable(). The old pmd_trans_huge() left a
tiny window for a race.
Useful applications are unlikely to notice the difference as doing
MADV_DONTNEED concurrently with a page fault would lead to undefined
behavior.
[js] 3.12 backport: no pmd_devmap in 3.12 yet.
[akpm@linux-foundation.org: tidy up comment grammar/layout]
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Reported-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | mm/memory.c | 14 |
1 files changed, 12 insertions, 2 deletions
diff --git a/mm/memory.c b/mm/memory.c index e9ddc7aceefa..43d45c9f7bf5 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3770,8 +3770,18 @@ static int __handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, if (unlikely(pmd_none(*pmd)) && unlikely(__pte_alloc(mm, vma, pmd, address))) return VM_FAULT_OOM; - /* if an huge pmd materialized from under us just retry later */ - if (unlikely(pmd_trans_huge(*pmd))) + /* + * If a huge pmd materialized under us just retry later. Use + * pmd_trans_unstable() instead of pmd_trans_huge() to ensure the pmd + * didn't become pmd_trans_huge under us and then back to pmd_none, as + * a result of MADV_DONTNEED running immediately after a huge pmd fault + * in a different thread of this mm, in turn leading to a misleading + * pmd_trans_huge() retval. All we have to ensure is that it is a + * regular pmd that we can walk with pte_offset_map() and we can do that + * through an atomic read in C, which is what pmd_trans_unstable() + * provides. + */ + if (unlikely(pmd_trans_unstable(pmd))) return 0; /* * A regular pmd is established and it can't morph into a huge pmd |