aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorMel Gorman <mgorman@suse.de>2013-01-11 17:31:40 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-01-11 17:54:54 -0500
commit04fa5d6a6547fbfcf613efd00637666fe19b24ab (patch)
tree5ef002ac18663361e53e323e0c9405d0c3f4593c /mm
parent0a1af1d61edae189b0a81bc46386ab37eb3d9d4d (diff)
mm: migrate: check page_count of THP before migrating
Hugh Dickins pointed out that migrate_misplaced_transhuge_page() does not check page_count before migrating like base page migration and khugepage. He could not see why this was safe and he is right. The potential impact of the bug is avoided due to the limitations of NUMA balancing. The page_mapcount() check ensures that only a single address space is using this page and as THPs are typically private it should not be possible for another address space to fault it in parallel. If the address space has one associated task then it's difficult to have both a GUP pin and be referencing the page at the same time. If there are multiple tasks then a buggy scenario requires that another thread be accessing the page while the direct IO is in flight. This is dodgy behaviour as there is a possibility of corruption with or without THP migration. It would be While we happen to be safe for the most part it is shoddy to depend on such "safety" so this patch checks the page count similar to anonymous pages. Note that this does not mean that the page_mapcount() check can go away. If we were to remove the page_mapcount() check the the THP would have to be unmapped from all referencing PTEs, replaced with migration PTEs and restored properly afterwards. Signed-off-by: Mel Gorman <mgorman@suse.de> Reported-by: Hugh Dickins <hughd@google.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Andrea Arcangeli <aarcange@redhat.com> Acked-by: Hugh Dickins <hughd@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/migrate.c14
1 files changed, 13 insertions, 1 deletions
diff --git a/mm/migrate.c b/mm/migrate.c
index 3b676b0c5c3e..c38778610aa8 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1679,9 +1679,21 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm,
1679 page_xchg_last_nid(new_page, page_last_nid(page)); 1679 page_xchg_last_nid(new_page, page_last_nid(page));
1680 1680
1681 isolated = numamigrate_isolate_page(pgdat, page); 1681 isolated = numamigrate_isolate_page(pgdat, page);
1682 if (!isolated) { 1682
1683 /*
1684 * Failing to isolate or a GUP pin prevents migration. The expected
1685 * page count is 2. 1 for anonymous pages without a mapping and 1
1686 * for the callers pin. If the page was isolated, the page will
1687 * need to be put back on the LRU.
1688 */
1689 if (!isolated || page_count(page) != 2) {
1683 count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR); 1690 count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR);
1684 put_page(new_page); 1691 put_page(new_page);
1692 if (isolated) {
1693 putback_lru_page(page);
1694 isolated = 0;
1695 goto out;
1696 }
1685 goto out_keep_locked; 1697 goto out_keep_locked;
1686 } 1698 }
1687 1699