diff options
| author | Dave Hansen <dave.hansen@linux.intel.com> | 2013-11-21 17:31:58 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-21 19:42:27 -0500 |
| commit | 30b0a105d9f7141e4cbf72ae5511832457d89788 (patch) | |
| tree | bbd52a86e93e43e7e5a2bfd234bffef9b796e055 /mm | |
| parent | c11230f44b3c3e055e4e7cd572fc1c4a22c6f4a9 (diff) | |
mm: thp: give transparent hugepage code a separate copy_page
Right now, the migration code in migrate_page_copy() uses copy_huge_page()
for hugetlbfs and thp pages:
if (PageHuge(page) || PageTransHuge(page))
copy_huge_page(newpage, page);
So, yay for code reuse. But:
void copy_huge_page(struct page *dst, struct page *src)
{
struct hstate *h = page_hstate(src);
and a non-hugetlbfs page has no page_hstate(). This works 99% of the
time because page_hstate() determines the hstate from the page order
alone. Since the page order of a THP page matches the default hugetlbfs
page order, it works.
But, if you change the default huge page size on the boot command-line
(say default_hugepagesz=1G), then we might not even *have* a 2MB hstate
so page_hstate() returns null and copy_huge_page() oopses pretty fast
since copy_huge_page() dereferences the hstate:
void copy_huge_page(struct page *dst, struct page *src)
{
struct hstate *h = page_hstate(src);
if (unlikely(pages_per_huge_page(h) > MAX_ORDER_NR_PAGES)) {
...
Mel noticed that the migration code is really the only user of these
functions. This moves all the copy code over to migrate.c and makes
copy_huge_page() work for THP by checking for it explicitly.
I believe the bug was introduced in commit b32967ff101a ("mm: numa: Add
THP migration for the NUMA working set scanning fault case")
[akpm@linux-foundation.org: fix coding-style and comment text, per Naoya Horiguchi]
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Acked-by: Mel Gorman <mgorman@suse.de>
Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Hillf Danton <dhillf@gmail.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Tested-by: Dave Jiang <dave.jiang@intel.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/hugetlb.c | 34 | ||||
| -rw-r--r-- | mm/migrate.c | 48 |
2 files changed, 48 insertions, 34 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 7d57af21f49e..2130365d387d 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c | |||
| @@ -476,40 +476,6 @@ static int vma_has_reserves(struct vm_area_struct *vma, long chg) | |||
| 476 | return 0; | 476 | return 0; |
| 477 | } | 477 | } |
| 478 | 478 | ||
| 479 | static void copy_gigantic_page(struct page *dst, struct page *src) | ||
| 480 | { | ||
| 481 | int i; | ||
| 482 | struct hstate *h = page_hstate(src); | ||
| 483 | struct page *dst_base = dst; | ||
| 484 | struct page *src_base = src; | ||
| 485 | |||
| 486 | for (i = 0; i < pages_per_huge_page(h); ) { | ||
| 487 | cond_resched(); | ||
| 488 | copy_highpage(dst, src); | ||
| 489 | |||
| 490 | i++; | ||
| 491 | dst = mem_map_next(dst, dst_base, i); | ||
| 492 | src = mem_map_next(src, src_base, i); | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | void copy_huge_page(struct page *dst, struct page *src) | ||
| 497 | { | ||
| 498 | int i; | ||
| 499 | struct hstate *h = page_hstate(src); | ||
| 500 | |||
| 501 | if (unlikely(pages_per_huge_page(h) > MAX_ORDER_NR_PAGES)) { | ||
| 502 | copy_gigantic_page(dst, src); | ||
| 503 | return; | ||
| 504 | } | ||
| 505 | |||
| 506 | might_sleep(); | ||
| 507 | for (i = 0; i < pages_per_huge_page(h); i++) { | ||
| 508 | cond_resched(); | ||
| 509 | copy_highpage(dst + i, src + i); | ||
| 510 | } | ||
| 511 | } | ||
| 512 | |||
| 513 | static void enqueue_huge_page(struct hstate *h, struct page *page) | 479 | static void enqueue_huge_page(struct hstate *h, struct page *page) |
| 514 | { | 480 | { |
| 515 | int nid = page_to_nid(page); | 481 | int nid = page_to_nid(page); |
diff --git a/mm/migrate.c b/mm/migrate.c index 316e720a2023..bb940045fe85 100644 --- a/mm/migrate.c +++ b/mm/migrate.c | |||
| @@ -442,6 +442,54 @@ int migrate_huge_page_move_mapping(struct address_space *mapping, | |||
| 442 | } | 442 | } |
| 443 | 443 | ||
| 444 | /* | 444 | /* |
| 445 | * Gigantic pages are so large that we do not guarantee that page++ pointer | ||
| 446 | * arithmetic will work across the entire page. We need something more | ||
| 447 | * specialized. | ||
| 448 | */ | ||
| 449 | static void __copy_gigantic_page(struct page *dst, struct page *src, | ||
| 450 | int nr_pages) | ||
| 451 | { | ||
| 452 | int i; | ||
| 453 | struct page *dst_base = dst; | ||
| 454 | struct page *src_base = src; | ||
| 455 | |||
| 456 | for (i = 0; i < nr_pages; ) { | ||
| 457 | cond_resched(); | ||
| 458 | copy_highpage(dst, src); | ||
| 459 | |||
| 460 | i++; | ||
| 461 | dst = mem_map_next(dst, dst_base, i); | ||
| 462 | src = mem_map_next(src, src_base, i); | ||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | static void copy_huge_page(struct page *dst, struct page *src) | ||
| 467 | { | ||
| 468 | int i; | ||
| 469 | int nr_pages; | ||
| 470 | |||
| 471 | if (PageHuge(src)) { | ||
| 472 | /* hugetlbfs page */ | ||
| 473 | struct hstate *h = page_hstate(src); | ||
| 474 | nr_pages = pages_per_huge_page(h); | ||
| 475 | |||
| 476 | if (unlikely(nr_pages > MAX_ORDER_NR_PAGES)) { | ||
| 477 | __copy_gigantic_page(dst, src, nr_pages); | ||
| 478 | return; | ||
| 479 | } | ||
| 480 | } else { | ||
| 481 | /* thp page */ | ||
| 482 | BUG_ON(!PageTransHuge(src)); | ||
| 483 | nr_pages = hpage_nr_pages(src); | ||
| 484 | } | ||
| 485 | |||
| 486 | for (i = 0; i < nr_pages; i++) { | ||
| 487 | cond_resched(); | ||
| 488 | copy_highpage(dst + i, src + i); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | /* | ||
| 445 | * Copy the page to its new location | 493 | * Copy the page to its new location |
| 446 | */ | 494 | */ |
| 447 | void migrate_page_copy(struct page *newpage, struct page *page) | 495 | void migrate_page_copy(struct page *newpage, struct page *page) |
