diff options
author | Kirill A. Shutemov <kirill.shutemov@linux.intel.com> | 2017-03-09 19:17:23 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-03-14 22:02:52 -0400 |
commit | 1771fc58a35d6c61861c08a190ecf13cd70a38eb (patch) | |
tree | 09a1bd5d7b7ca358bc45ecfdbc7c4cff2b7ea877 | |
parent | 2f18b39499b23579a60e8ed1d2c0b56154388323 (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.c | 9 |
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); |