diff options
author | Hugh Dickins <hughd@google.com> | 2017-04-07 19:04:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-04-08 03:47:48 -0400 |
commit | d75450ff40df0199bf13dfb19f435519ff947138 (patch) | |
tree | 3eac1374291bf826d4af0c616e47f7ec4f7b4fe4 | |
parent | 81d4bab4ce87228c37ab14a885438544af5c9ce6 (diff) |
mm: fix page_vma_mapped_walk() for ksm pages
Doug Smythies reports oops with KSM in this backtrace, I've been seeing
the same:
page_vma_mapped_walk+0xe6/0x5b0
page_referenced_one+0x91/0x1a0
rmap_walk_ksm+0x100/0x190
rmap_walk+0x4f/0x60
page_referenced+0x149/0x170
shrink_active_list+0x1c2/0x430
shrink_node_memcg+0x67a/0x7a0
shrink_node+0xe1/0x320
kswapd+0x34b/0x720
Just as observed in commit 4b0ece6fa016 ("mm: migrate: fix
remove_migration_pte() for ksm pages"), you cannot use page->index
calculations on ksm pages.
page_vma_mapped_walk() is relying on __vma_address(), where a ksm page
can lead it off the end of the page table, and into whatever nonsense is
in the next page, ending as an oops inside check_pte()'s pte_page().
KSM tells page_vma_mapped_walk() exactly where to look for the page, it
does not need any page->index calculation: and that's so also for all
the normal and file and anon pages - just not for THPs and their
subpages. Get out early in most cases: instead of a PageKsm test, move
down the earlier not-THP-page test, as suggested by Kirill.
I'm also slightly worried that this loop can stray into other vmas, so
added a vm_end test to prevent surprises; though I have not imagined
anything worse than a very contrived case, in which a page mlocked in
the next vma might be reclaimed because it is not mlocked in this vma.
Fixes: ace71a19cec5 ("mm: introduce page_vma_mapped_walk()")
Link: http://lkml.kernel.org/r/alpine.LSU.2.11.1704031104400.1118@eggly.anvils
Signed-off-by: Hugh Dickins <hughd@google.com>
Reported-by: Doug Smythies <dsmythies@telus.net>
Tested-by: Doug Smythies <dsmythies@telus.net>
Reviewed-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>
-rw-r--r-- | mm/page_vma_mapped.c | 15 |
1 files changed, 8 insertions, 7 deletions
diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c index c4c9def8ffea..de9c40d7304a 100644 --- a/mm/page_vma_mapped.c +++ b/mm/page_vma_mapped.c | |||
@@ -111,12 +111,8 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw) | |||
111 | if (pvmw->pmd && !pvmw->pte) | 111 | if (pvmw->pmd && !pvmw->pte) |
112 | return not_found(pvmw); | 112 | return not_found(pvmw); |
113 | 113 | ||
114 | /* Only for THP, seek to next pte entry makes sense */ | 114 | if (pvmw->pte) |
115 | if (pvmw->pte) { | ||
116 | if (!PageTransHuge(pvmw->page) || PageHuge(pvmw->page)) | ||
117 | return not_found(pvmw); | ||
118 | goto next_pte; | 115 | goto next_pte; |
119 | } | ||
120 | 116 | ||
121 | if (unlikely(PageHuge(pvmw->page))) { | 117 | if (unlikely(PageHuge(pvmw->page))) { |
122 | /* when pud is not present, pte will be NULL */ | 118 | /* when pud is not present, pte will be NULL */ |
@@ -165,9 +161,14 @@ restart: | |||
165 | while (1) { | 161 | while (1) { |
166 | if (check_pte(pvmw)) | 162 | if (check_pte(pvmw)) |
167 | return true; | 163 | return true; |
168 | next_pte: do { | 164 | next_pte: |
165 | /* Seek to next pte only makes sense for THP */ | ||
166 | if (!PageTransHuge(pvmw->page) || PageHuge(pvmw->page)) | ||
167 | return not_found(pvmw); | ||
168 | do { | ||
169 | pvmw->address += PAGE_SIZE; | 169 | pvmw->address += PAGE_SIZE; |
170 | if (pvmw->address >= | 170 | if (pvmw->address >= pvmw->vma->vm_end || |
171 | pvmw->address >= | ||
171 | __vma_address(pvmw->page, pvmw->vma) + | 172 | __vma_address(pvmw->page, pvmw->vma) + |
172 | hpage_nr_pages(pvmw->page) * PAGE_SIZE) | 173 | hpage_nr_pages(pvmw->page) * PAGE_SIZE) |
173 | return not_found(pvmw); | 174 | return not_found(pvmw); |