aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>2016-07-26 18:25:53 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-26 19:19:19 -0400
commit9a73f61bdb8acdc01bbaf72a3fe0a8854f2463ad (patch)
treeadd475877423bc5ae648e67d221324ceaa388fcf
parentbaa355fd331424526e742d41d9b90d5f9d10f716 (diff)
thp, mlock: do not mlock PTE-mapped file huge pages
As with anon THP, we only mlock file huge pages if we can prove that the page is not mapped with PTE. This way we can avoid mlock leak into non-mlocked vma on split. We rely on PageDoubleMap() under lock_page() to check if the the page may be PTE mapped. PG_double_map is set by page_add_file_rmap() when the page mapped with PTEs. Link: http://lkml.kernel.org/r/1466021202-61880-21-git-send-email-kirill.shutemov@linux.intel.com Signed-off-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--include/linux/page-flags.h13
-rw-r--r--mm/huge_memory.c27
-rw-r--r--mm/mmap.c6
-rw-r--r--mm/page_alloc.c2
-rw-r--r--mm/rmap.c16
5 files changed, 54 insertions, 10 deletions
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 7c8e82ac2eb7..8cf09639185a 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -581,6 +581,17 @@ static inline int PageDoubleMap(struct page *page)
581 return PageHead(page) && test_bit(PG_double_map, &page[1].flags); 581 return PageHead(page) && test_bit(PG_double_map, &page[1].flags);
582} 582}
583 583
584static inline void SetPageDoubleMap(struct page *page)
585{
586 VM_BUG_ON_PAGE(!PageHead(page), page);
587 set_bit(PG_double_map, &page[1].flags);
588}
589
590static inline void ClearPageDoubleMap(struct page *page)
591{
592 VM_BUG_ON_PAGE(!PageHead(page), page);
593 clear_bit(PG_double_map, &page[1].flags);
594}
584static inline int TestSetPageDoubleMap(struct page *page) 595static inline int TestSetPageDoubleMap(struct page *page)
585{ 596{
586 VM_BUG_ON_PAGE(!PageHead(page), page); 597 VM_BUG_ON_PAGE(!PageHead(page), page);
@@ -598,7 +609,7 @@ TESTPAGEFLAG_FALSE(TransHuge)
598TESTPAGEFLAG_FALSE(TransCompound) 609TESTPAGEFLAG_FALSE(TransCompound)
599TESTPAGEFLAG_FALSE(TransCompoundMap) 610TESTPAGEFLAG_FALSE(TransCompoundMap)
600TESTPAGEFLAG_FALSE(TransTail) 611TESTPAGEFLAG_FALSE(TransTail)
601TESTPAGEFLAG_FALSE(DoubleMap) 612PAGEFLAG_FALSE(DoubleMap)
602 TESTSETFLAG_FALSE(DoubleMap) 613 TESTSETFLAG_FALSE(DoubleMap)
603 TESTCLEARFLAG_FALSE(DoubleMap) 614 TESTCLEARFLAG_FALSE(DoubleMap)
604#endif 615#endif
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 486077742650..3b74fea6b5db 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1437,6 +1437,8 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
1437 * We don't mlock() pte-mapped THPs. This way we can avoid 1437 * We don't mlock() pte-mapped THPs. This way we can avoid
1438 * leaking mlocked pages into non-VM_LOCKED VMAs. 1438 * leaking mlocked pages into non-VM_LOCKED VMAs.
1439 * 1439 *
1440 * For anon THP:
1441 *
1440 * In most cases the pmd is the only mapping of the page as we 1442 * In most cases the pmd is the only mapping of the page as we
1441 * break COW for the mlock() -- see gup_flags |= FOLL_WRITE for 1443 * break COW for the mlock() -- see gup_flags |= FOLL_WRITE for
1442 * writable private mappings in populate_vma_page_range(). 1444 * writable private mappings in populate_vma_page_range().
@@ -1444,15 +1446,26 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
1444 * The only scenario when we have the page shared here is if we 1446 * The only scenario when we have the page shared here is if we
1445 * mlocking read-only mapping shared over fork(). We skip 1447 * mlocking read-only mapping shared over fork(). We skip
1446 * mlocking such pages. 1448 * mlocking such pages.
1449 *
1450 * For file THP:
1451 *
1452 * We can expect PageDoubleMap() to be stable under page lock:
1453 * for file pages we set it in page_add_file_rmap(), which
1454 * requires page to be locked.
1447 */ 1455 */
1448 if (compound_mapcount(page) == 1 && !PageDoubleMap(page) && 1456
1449 page->mapping && trylock_page(page)) { 1457 if (PageAnon(page) && compound_mapcount(page) != 1)
1450 lru_add_drain(); 1458 goto skip_mlock;
1451 if (page->mapping) 1459 if (PageDoubleMap(page) || !page->mapping)
1452 mlock_vma_page(page); 1460 goto skip_mlock;
1453 unlock_page(page); 1461 if (!trylock_page(page))
1454 } 1462 goto skip_mlock;
1463 lru_add_drain();
1464 if (page->mapping && !PageDoubleMap(page))
1465 mlock_vma_page(page);
1466 unlock_page(page);
1455 } 1467 }
1468skip_mlock:
1456 page += (addr & ~HPAGE_PMD_MASK) >> PAGE_SHIFT; 1469 page += (addr & ~HPAGE_PMD_MASK) >> PAGE_SHIFT;
1457 VM_BUG_ON_PAGE(!PageCompound(page), page); 1470 VM_BUG_ON_PAGE(!PageCompound(page), page);
1458 if (flags & FOLL_GET) 1471 if (flags & FOLL_GET)
diff --git a/mm/mmap.c b/mm/mmap.c
index 31f9b2220b72..a41872c8f2af 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2591,6 +2591,12 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
2591 /* drop PG_Mlocked flag for over-mapped range */ 2591 /* drop PG_Mlocked flag for over-mapped range */
2592 for (tmp = vma; tmp->vm_start >= start + size; 2592 for (tmp = vma; tmp->vm_start >= start + size;
2593 tmp = tmp->vm_next) { 2593 tmp = tmp->vm_next) {
2594 /*
2595 * Split pmd and munlock page on the border
2596 * of the range.
2597 */
2598 vma_adjust_trans_huge(tmp, start, start + size, 0);
2599
2594 munlock_vma_pages_range(tmp, 2600 munlock_vma_pages_range(tmp,
2595 max(tmp->vm_start, start), 2601 max(tmp->vm_start, start),
2596 min(tmp->vm_end, start + size)); 2602 min(tmp->vm_end, start + size));
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 7023a31edc5c..847281eb74da 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1007,6 +1007,8 @@ static __always_inline bool free_pages_prepare(struct page *page,
1007 1007
1008 VM_BUG_ON_PAGE(compound && compound_order(page) != order, page); 1008 VM_BUG_ON_PAGE(compound && compound_order(page) != order, page);
1009 1009
1010 if (compound)
1011 ClearPageDoubleMap(page);
1010 for (i = 1; i < (1 << order); i++) { 1012 for (i = 1; i < (1 << order); i++) {
1011 if (compound) 1013 if (compound)
1012 bad += free_tail_pages_check(page, page + i); 1014 bad += free_tail_pages_check(page, page + i);
diff --git a/mm/rmap.c b/mm/rmap.c
index 2b336c4277da..9d643b7a99ce 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1284,6 +1284,12 @@ void page_add_file_rmap(struct page *page, bool compound)
1284 if (!atomic_inc_and_test(compound_mapcount_ptr(page))) 1284 if (!atomic_inc_and_test(compound_mapcount_ptr(page)))
1285 goto out; 1285 goto out;
1286 } else { 1286 } else {
1287 if (PageTransCompound(page)) {
1288 VM_BUG_ON_PAGE(!PageLocked(page), page);
1289 SetPageDoubleMap(compound_head(page));
1290 if (PageMlocked(page))
1291 clear_page_mlock(compound_head(page));
1292 }
1287 if (!atomic_inc_and_test(&page->_mapcount)) 1293 if (!atomic_inc_and_test(&page->_mapcount))
1288 goto out; 1294 goto out;
1289 } 1295 }
@@ -1458,8 +1464,14 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
1458 */ 1464 */
1459 if (!(flags & TTU_IGNORE_MLOCK)) { 1465 if (!(flags & TTU_IGNORE_MLOCK)) {
1460 if (vma->vm_flags & VM_LOCKED) { 1466 if (vma->vm_flags & VM_LOCKED) {
1461 /* Holding pte lock, we do *not* need mmap_sem here */ 1467 /* PTE-mapped THP are never mlocked */
1462 mlock_vma_page(page); 1468 if (!PageTransCompound(page)) {
1469 /*
1470 * Holding pte lock, we do *not* need
1471 * mmap_sem here
1472 */
1473 mlock_vma_page(page);
1474 }
1463 ret = SWAP_MLOCK; 1475 ret = SWAP_MLOCK;
1464 goto out_unmap; 1476 goto out_unmap;
1465 } 1477 }