summaryrefslogtreecommitdiffstats
path: root/mm/rmap.c
diff options
context:
space:
mode:
authorMike Kravetz <mike.kravetz@oracle.com>2018-10-05 18:51:29 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-10-05 19:32:04 -0400
commit017b1660df89f5fb4bfe66c34e35f7d2031100c7 (patch)
tree3c12fd67a673e96b93e7f0f2f78379ff080d9744 /mm/rmap.c
parentb2e45b46d85bfc67b7b28e0e0c1c7e9b9a35892c (diff)
mm: migration: fix migration of huge PMD shared pages
The page migration code employs try_to_unmap() to try and unmap the source page. This is accomplished by using rmap_walk to find all vmas where the page is mapped. This search stops when page mapcount is zero. For shared PMD huge pages, the page map count is always 1 no matter the number of mappings. Shared mappings are tracked via the reference count of the PMD page. Therefore, try_to_unmap stops prematurely and does not completely unmap all mappings of the source page. This problem can result is data corruption as writes to the original source page can happen after contents of the page are copied to the target page. Hence, data is lost. This problem was originally seen as DB corruption of shared global areas after a huge page was soft offlined due to ECC memory errors. DB developers noticed they could reproduce the issue by (hotplug) offlining memory used to back huge pages. A simple testcase can reproduce the problem by creating a shared PMD mapping (note that this must be at least PUD_SIZE in size and PUD_SIZE aligned (1GB on x86)), and using migrate_pages() to migrate process pages between nodes while continually writing to the huge pages being migrated. To fix, have the try_to_unmap_one routine check for huge PMD sharing by calling huge_pmd_unshare for hugetlbfs huge pages. If it is a shared mapping it will be 'unshared' which removes the page table entry and drops the reference on the PMD page. After this, flush caches and TLB. mmu notifiers are called before locking page tables, but we can not be sure of PMD sharing until page tables are locked. Therefore, check for the possibility of PMD sharing before locking so that notifiers can prepare for the worst possible case. Link: http://lkml.kernel.org/r/20180823205917.16297-2-mike.kravetz@oracle.com [mike.kravetz@oracle.com: make _range_in_vma() a static inline] Link: http://lkml.kernel.org/r/6063f215-a5c8-2f0c-465a-2c515ddc952d@oracle.com Fixes: 39dde65c9940 ("shared page table for hugetlb page") Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com> Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Acked-by: Michal Hocko <mhocko@suse.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: Jerome Glisse <jglisse@redhat.com> Cc: Mike Kravetz <mike.kravetz@oracle.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'mm/rmap.c')
-rw-r--r--mm/rmap.c42
1 files changed, 39 insertions, 3 deletions
diff --git a/mm/rmap.c b/mm/rmap.c
index eb477809a5c0..1e79fac3186b 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1362,11 +1362,21 @@ static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
1362 } 1362 }
1363 1363
1364 /* 1364 /*
1365 * We have to assume the worse case ie pmd for invalidation. Note that 1365 * For THP, we have to assume the worse case ie pmd for invalidation.
1366 * the page can not be free in this function as call of try_to_unmap() 1366 * For hugetlb, it could be much worse if we need to do pud
1367 * must hold a reference on the page. 1367 * invalidation in the case of pmd sharing.
1368 *
1369 * Note that the page can not be free in this function as call of
1370 * try_to_unmap() must hold a reference on the page.
1368 */ 1371 */
1369 end = min(vma->vm_end, start + (PAGE_SIZE << compound_order(page))); 1372 end = min(vma->vm_end, start + (PAGE_SIZE << compound_order(page)));
1373 if (PageHuge(page)) {
1374 /*
1375 * If sharing is possible, start and end will be adjusted
1376 * accordingly.
1377 */
1378 adjust_range_if_pmd_sharing_possible(vma, &start, &end);
1379 }
1370 mmu_notifier_invalidate_range_start(vma->vm_mm, start, end); 1380 mmu_notifier_invalidate_range_start(vma->vm_mm, start, end);
1371 1381
1372 while (page_vma_mapped_walk(&pvmw)) { 1382 while (page_vma_mapped_walk(&pvmw)) {
@@ -1409,6 +1419,32 @@ static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
1409 subpage = page - page_to_pfn(page) + pte_pfn(*pvmw.pte); 1419 subpage = page - page_to_pfn(page) + pte_pfn(*pvmw.pte);
1410 address = pvmw.address; 1420 address = pvmw.address;
1411 1421
1422 if (PageHuge(page)) {
1423 if (huge_pmd_unshare(mm, &address, pvmw.pte)) {
1424 /*
1425 * huge_pmd_unshare unmapped an entire PMD
1426 * page. There is no way of knowing exactly
1427 * which PMDs may be cached for this mm, so
1428 * we must flush them all. start/end were
1429 * already adjusted above to cover this range.
1430 */
1431 flush_cache_range(vma, start, end);
1432 flush_tlb_range(vma, start, end);
1433 mmu_notifier_invalidate_range(mm, start, end);
1434
1435 /*
1436 * The ref count of the PMD page was dropped
1437 * which is part of the way map counting
1438 * is done for shared PMDs. Return 'true'
1439 * here. When there is no other sharing,
1440 * huge_pmd_unshare returns false and we will
1441 * unmap the actual page and drop map count
1442 * to zero.
1443 */
1444 page_vma_mapped_walk_done(&pvmw);
1445 break;
1446 }
1447 }
1412 1448
1413 if (IS_ENABLED(CONFIG_MIGRATION) && 1449 if (IS_ENABLED(CONFIG_MIGRATION) &&
1414 (flags & TTU_MIGRATION) && 1450 (flags & TTU_MIGRATION) &&