diff options
Diffstat (limited to 'mm/ksm.c')
-rw-r--r-- | mm/ksm.c | 67 |
1 files changed, 58 insertions, 9 deletions
@@ -412,6 +412,29 @@ out: | |||
412 | up_read(&mm->mmap_sem); | 412 | up_read(&mm->mmap_sem); |
413 | } | 413 | } |
414 | 414 | ||
415 | static struct page *page_trans_compound_anon(struct page *page) | ||
416 | { | ||
417 | if (PageTransCompound(page)) { | ||
418 | struct page *head; | ||
419 | head = compound_head(page); | ||
420 | /* | ||
421 | * head may be a dangling pointer. | ||
422 | * __split_huge_page_refcount clears PageTail | ||
423 | * before overwriting first_page, so if | ||
424 | * PageTail is still there it means the head | ||
425 | * pointer isn't dangling. | ||
426 | */ | ||
427 | if (head != page) { | ||
428 | smp_rmb(); | ||
429 | if (!PageTransCompound(page)) | ||
430 | return NULL; | ||
431 | } | ||
432 | if (PageAnon(head)) | ||
433 | return head; | ||
434 | } | ||
435 | return NULL; | ||
436 | } | ||
437 | |||
415 | static struct page *get_mergeable_page(struct rmap_item *rmap_item) | 438 | static struct page *get_mergeable_page(struct rmap_item *rmap_item) |
416 | { | 439 | { |
417 | struct mm_struct *mm = rmap_item->mm; | 440 | struct mm_struct *mm = rmap_item->mm; |
@@ -431,7 +454,7 @@ static struct page *get_mergeable_page(struct rmap_item *rmap_item) | |||
431 | page = follow_page(vma, addr, FOLL_GET); | 454 | page = follow_page(vma, addr, FOLL_GET); |
432 | if (IS_ERR_OR_NULL(page)) | 455 | if (IS_ERR_OR_NULL(page)) |
433 | goto out; | 456 | goto out; |
434 | if (PageAnon(page) && !PageTransCompound(page)) { | 457 | if (PageAnon(page) || page_trans_compound_anon(page)) { |
435 | flush_anon_page(vma, page, addr); | 458 | flush_anon_page(vma, page, addr); |
436 | flush_dcache_page(page); | 459 | flush_dcache_page(page); |
437 | } else { | 460 | } else { |
@@ -709,6 +732,7 @@ static int write_protect_page(struct vm_area_struct *vma, struct page *page, | |||
709 | if (addr == -EFAULT) | 732 | if (addr == -EFAULT) |
710 | goto out; | 733 | goto out; |
711 | 734 | ||
735 | BUG_ON(PageTransCompound(page)); | ||
712 | ptep = page_check_address(page, mm, addr, &ptl, 0); | 736 | ptep = page_check_address(page, mm, addr, &ptl, 0); |
713 | if (!ptep) | 737 | if (!ptep) |
714 | goto out; | 738 | goto out; |
@@ -784,6 +808,7 @@ static int replace_page(struct vm_area_struct *vma, struct page *page, | |||
784 | goto out; | 808 | goto out; |
785 | 809 | ||
786 | pmd = pmd_offset(pud, addr); | 810 | pmd = pmd_offset(pud, addr); |
811 | BUG_ON(pmd_trans_huge(*pmd)); | ||
787 | if (!pmd_present(*pmd)) | 812 | if (!pmd_present(*pmd)) |
788 | goto out; | 813 | goto out; |
789 | 814 | ||
@@ -811,6 +836,33 @@ out: | |||
811 | return err; | 836 | return err; |
812 | } | 837 | } |
813 | 838 | ||
839 | static int page_trans_compound_anon_split(struct page *page) | ||
840 | { | ||
841 | int ret = 0; | ||
842 | struct page *transhuge_head = page_trans_compound_anon(page); | ||
843 | if (transhuge_head) { | ||
844 | /* Get the reference on the head to split it. */ | ||
845 | if (get_page_unless_zero(transhuge_head)) { | ||
846 | /* | ||
847 | * Recheck we got the reference while the head | ||
848 | * was still anonymous. | ||
849 | */ | ||
850 | if (PageAnon(transhuge_head)) | ||
851 | ret = split_huge_page(transhuge_head); | ||
852 | else | ||
853 | /* | ||
854 | * Retry later if split_huge_page run | ||
855 | * from under us. | ||
856 | */ | ||
857 | ret = 1; | ||
858 | put_page(transhuge_head); | ||
859 | } else | ||
860 | /* Retry later if split_huge_page run from under us. */ | ||
861 | ret = 1; | ||
862 | } | ||
863 | return ret; | ||
864 | } | ||
865 | |||
814 | /* | 866 | /* |
815 | * try_to_merge_one_page - take two pages and merge them into one | 867 | * try_to_merge_one_page - take two pages and merge them into one |
816 | * @vma: the vma that holds the pte pointing to page | 868 | * @vma: the vma that holds the pte pointing to page |
@@ -831,6 +883,9 @@ static int try_to_merge_one_page(struct vm_area_struct *vma, | |||
831 | 883 | ||
832 | if (!(vma->vm_flags & VM_MERGEABLE)) | 884 | if (!(vma->vm_flags & VM_MERGEABLE)) |
833 | goto out; | 885 | goto out; |
886 | if (PageTransCompound(page) && page_trans_compound_anon_split(page)) | ||
887 | goto out; | ||
888 | BUG_ON(PageTransCompound(page)); | ||
834 | if (!PageAnon(page)) | 889 | if (!PageAnon(page)) |
835 | goto out; | 890 | goto out; |
836 | 891 | ||
@@ -1285,14 +1340,8 @@ next_mm: | |||
1285 | cond_resched(); | 1340 | cond_resched(); |
1286 | continue; | 1341 | continue; |
1287 | } | 1342 | } |
1288 | if (PageTransCompound(*page)) { | 1343 | if (PageAnon(*page) || |
1289 | put_page(*page); | 1344 | page_trans_compound_anon(*page)) { |
1290 | ksm_scan.address &= HPAGE_PMD_MASK; | ||
1291 | ksm_scan.address += HPAGE_PMD_SIZE; | ||
1292 | cond_resched(); | ||
1293 | continue; | ||
1294 | } | ||
1295 | if (PageAnon(*page)) { | ||
1296 | flush_anon_page(vma, *page, ksm_scan.address); | 1345 | flush_anon_page(vma, *page, ksm_scan.address); |
1297 | flush_dcache_page(*page); | 1346 | flush_dcache_page(*page); |
1298 | rmap_item = get_next_rmap_item(slot, | 1347 | rmap_item = get_next_rmap_item(slot, |