Skip to content

Commit

Permalink
thp: KSM on THP
Browse files Browse the repository at this point in the history
This makes KSM full operational with THP pages.  Subpages are scanned
while the hugepage is still in place and delivering max cpu performance,
and only if there's a match and we're going to deduplicate memory, the
single hugepages with the subpage match is split.

There will be no false sharing between ksmd and khugepaged.  khugepaged
won't collapse 2m virtual regions with KSM pages inside.  ksmd also should
only split pages when the checksum matches and we're likely to split an
hugepage for some long living ksm page (usual ksm heuristic to avoid
sharing pages that get de-cowed).

Signed-off-by: Andrea Arcangeli <[email protected]>
Cc: Hugh Dickins <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
aagit authored and torvalds committed Jan 14, 2011
1 parent 60ab324 commit 29ad768
Showing 1 changed file with 58 additions and 9 deletions.
67 changes: 58 additions & 9 deletions mm/ksm.c
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,29 @@ static void break_cow(struct rmap_item *rmap_item)
up_read(&mm->mmap_sem);
}

static struct page *page_trans_compound_anon(struct page *page)
{
if (PageTransCompound(page)) {
struct page *head;
head = compound_head(page);
/*
* head may be a dangling pointer.
* __split_huge_page_refcount clears PageTail
* before overwriting first_page, so if
* PageTail is still there it means the head
* pointer isn't dangling.
*/
if (head != page) {
smp_rmb();
if (!PageTransCompound(page))
return NULL;
}
if (PageAnon(head))
return head;
}
return NULL;
}

static struct page *get_mergeable_page(struct rmap_item *rmap_item)
{
struct mm_struct *mm = rmap_item->mm;
Expand All @@ -431,7 +454,7 @@ static struct page *get_mergeable_page(struct rmap_item *rmap_item)
page = follow_page(vma, addr, FOLL_GET);
if (IS_ERR_OR_NULL(page))
goto out;
if (PageAnon(page) && !PageTransCompound(page)) {
if (PageAnon(page) || page_trans_compound_anon(page)) {
flush_anon_page(vma, page, addr);
flush_dcache_page(page);
} else {
Expand Down Expand Up @@ -709,6 +732,7 @@ static int write_protect_page(struct vm_area_struct *vma, struct page *page,
if (addr == -EFAULT)
goto out;

BUG_ON(PageTransCompound(page));
ptep = page_check_address(page, mm, addr, &ptl, 0);
if (!ptep)
goto out;
Expand Down Expand Up @@ -784,6 +808,7 @@ static int replace_page(struct vm_area_struct *vma, struct page *page,
goto out;

pmd = pmd_offset(pud, addr);
BUG_ON(pmd_trans_huge(*pmd));
if (!pmd_present(*pmd))
goto out;

Expand Down Expand Up @@ -811,6 +836,33 @@ static int replace_page(struct vm_area_struct *vma, struct page *page,
return err;
}

static int page_trans_compound_anon_split(struct page *page)
{
int ret = 0;
struct page *transhuge_head = page_trans_compound_anon(page);
if (transhuge_head) {
/* Get the reference on the head to split it. */
if (get_page_unless_zero(transhuge_head)) {
/*
* Recheck we got the reference while the head
* was still anonymous.
*/
if (PageAnon(transhuge_head))
ret = split_huge_page(transhuge_head);
else
/*
* Retry later if split_huge_page run
* from under us.
*/
ret = 1;
put_page(transhuge_head);
} else
/* Retry later if split_huge_page run from under us. */
ret = 1;
}
return ret;
}

/*
* try_to_merge_one_page - take two pages and merge them into one
* @vma: the vma that holds the pte pointing to page
Expand All @@ -831,6 +883,9 @@ static int try_to_merge_one_page(struct vm_area_struct *vma,

if (!(vma->vm_flags & VM_MERGEABLE))
goto out;
if (PageTransCompound(page) && page_trans_compound_anon_split(page))
goto out;
BUG_ON(PageTransCompound(page));
if (!PageAnon(page))
goto out;

Expand Down Expand Up @@ -1285,14 +1340,8 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page)
cond_resched();
continue;
}
if (PageTransCompound(*page)) {
put_page(*page);
ksm_scan.address &= HPAGE_PMD_MASK;
ksm_scan.address += HPAGE_PMD_SIZE;
cond_resched();
continue;
}
if (PageAnon(*page)) {
if (PageAnon(*page) ||
page_trans_compound_anon(*page)) {
flush_anon_page(vma, *page, ksm_scan.address);
flush_dcache_page(*page);
rmap_item = get_next_rmap_item(slot,
Expand Down

0 comments on commit 29ad768

Please sign in to comment.