aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>2017-03-09 19:17:23 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-03-14 22:02:52 -0400
commit1771fc58a35d6c61861c08a190ecf13cd70a38eb (patch)
tree09a1bd5d7b7ca358bc45ecfdbc7c4cff2b7ea877
parent2f18b39499b23579a60e8ed1d2c0b56154388323 (diff)
thp: fix another corner case of munlock() vs. THPs
commit 6ebb4a1b848fe75323135f93e72c78f8780fd268 upstream. The following test case triggers BUG() in munlock_vma_pages_range(): int main(int argc, char *argv[]) { int fd; system("mount -t tmpfs -o huge=always none /mnt"); fd = open("/mnt/test", O_CREAT | O_RDWR); ftruncate(fd, 4UL << 20); mmap(NULL, 4UL << 20, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_LOCKED, fd, 0); mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, 0); munlockall(); return 0; } The second mmap() create PTE-mapping of the first huge page in file. It makes kernel munlock the page as we never keep PTE-mapped page mlocked. On munlockall() when we handle vma created by the first mmap(), munlock_vma_page() returns page_mask == 0, as the page is not mlocked anymore. On next iteration follow_page_mask() return tail page, but page_mask is HPAGE_NR_PAGES - 1. It makes us skip to the first tail page of the next huge page and step on VM_BUG_ON_PAGE(PageMlocked(page)). The fix is not use the page_mask from follow_page_mask() at all. It has no use for us. Link: http://lkml.kernel.org/r/20170302150252.34120-1-kirill.shutemov@linux.intel.com Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Cc: Andrea Arcangeli <aarcange@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--mm/mlock.c9
1 files changed, 4 insertions, 5 deletions
diff --git a/mm/mlock.c b/mm/mlock.c
index cdbed8aaa426..665ab75b5533 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -441,7 +441,7 @@ void munlock_vma_pages_range(struct vm_area_struct *vma,
441 441
442 while (start < end) { 442 while (start < end) {
443 struct page *page; 443 struct page *page;
444 unsigned int page_mask; 444 unsigned int page_mask = 0;
445 unsigned long page_increm; 445 unsigned long page_increm;
446 struct pagevec pvec; 446 struct pagevec pvec;
447 struct zone *zone; 447 struct zone *zone;
@@ -455,8 +455,7 @@ void munlock_vma_pages_range(struct vm_area_struct *vma,
455 * suits munlock very well (and if somehow an abnormal page 455 * suits munlock very well (and if somehow an abnormal page
456 * has sneaked into the range, we won't oops here: great). 456 * has sneaked into the range, we won't oops here: great).
457 */ 457 */
458 page = follow_page_mask(vma, start, FOLL_GET | FOLL_DUMP, 458 page = follow_page(vma, start, FOLL_GET | FOLL_DUMP);
459 &page_mask);
460 459
461 if (page && !IS_ERR(page)) { 460 if (page && !IS_ERR(page)) {
462 if (PageTransTail(page)) { 461 if (PageTransTail(page)) {
@@ -467,8 +466,8 @@ void munlock_vma_pages_range(struct vm_area_struct *vma,
467 /* 466 /*
468 * Any THP page found by follow_page_mask() may 467 * Any THP page found by follow_page_mask() may
469 * have gotten split before reaching 468 * have gotten split before reaching
470 * munlock_vma_page(), so we need to recompute 469 * munlock_vma_page(), so we need to compute
471 * the page_mask here. 470 * the page_mask here instead.
472 */ 471 */
473 page_mask = munlock_vma_page(page); 472 page_mask = munlock_vma_page(page);
474 unlock_page(page); 473 unlock_page(page);