Subject: page anon_vma From: Andrea Arcangeli Find the anon_vma to lock from the page rather than from the vma, after recent anon_vma changes that allows a vma to belong to more than a single anon_vma. Signed-off-by: Andrea Arcangeli --- mm/huge_memory.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -762,13 +762,14 @@ static void __split_huge_page(struct pag struct anon_vma *anon_vma) { int mapcount, mapcount2; - struct vm_area_struct *vma; + struct anon_vma_chain *avc; BUG_ON(!PageHead(page)); BUG_ON(PageTail(page)); mapcount = 0; - list_for_each_entry(vma, &anon_vma->head, anon_vma_node) { + list_for_each_entry(avc, &anon_vma->head, same_anon_vma) { + struct vm_area_struct *vma = avc->vma; unsigned long addr = vma_address(page, vma); if (addr == -EFAULT) continue; @@ -779,7 +780,8 @@ static void __split_huge_page(struct pag __split_huge_page_refcount(page); mapcount2 = 0; - list_for_each_entry(vma, &anon_vma->head, anon_vma_node) { + list_for_each_entry(avc, &anon_vma->head, same_anon_vma) { + struct vm_area_struct *vma = avc->vma; unsigned long addr = vma_address(page, vma); if (addr == -EFAULT) continue; @@ -792,29 +794,29 @@ static void __split_huge_page(struct pag void __split_huge_page_vma(struct vm_area_struct *vma, pmd_t *pmd) { struct page *page; - struct anon_vma *anon_vma; struct mm_struct *mm; BUG_ON(vma->vm_flags & VM_HUGETLB); mm = vma->vm_mm; - anon_vma = vma->anon_vma; - - spin_lock(&anon_vma->lock); - BUG_ON(pmd_trans_splitting(*pmd)); spin_lock(&mm->page_table_lock); if (unlikely(!pmd_trans_huge(*pmd))) { spin_unlock(&mm->page_table_lock); - spin_unlock(&anon_vma->lock); return; } page = pmd_page(*pmd); + VM_BUG_ON(!page_count(page)); + get_page(page); spin_unlock(&mm->page_table_lock); - __split_huge_page(page, anon_vma); + /* + * The vma->anon_vma->lock is the wrong lock if the page is shared, + * the anon_vma->lock pointed by page->mapping is the right one. + */ + split_huge_page(page); - spin_unlock(&anon_vma->lock); + put_page(page); BUG_ON(pmd_trans_huge(*pmd)); }