Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: fix migration hangs on anon_vma lock

Increased usage of page migration in mmotm reveals that the anon_vma
locking in unmap_and_move() has been deficient since 2.6.36 (or even
earlier). Review at the time of f18194275c39835cb84563500995e0d503a32d9a
("mm: fix hang on anon_vma->root->lock") missed the issue here: the
anon_vma to which we get a reference may already have been freed back to
its slab (it is in use when we check page_mapped, but that can change),
and so its anon_vma->root may be switched at any moment by reuse in
anon_vma_prepare.

Perhaps we could fix that with a get_anon_vma_unless_zero(), but let's
not: just rely on page_lock_anon_vma() to do all the hard thinking for us,
then we don't need any rcu read locking over here.

In removing the rcu_unlock label: since PageAnon is a bit in
page->mapping, it's impossible for a !page->mapping page to be anon; but
insert VM_BUG_ON in case the implementation ever changes.

[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Hugh Dickins <hughd@google.com>
Reviewed-by: Mel Gorman <mel@csn.ul.ie>
Reviewed-by: Rik van Riel <riel@redhat.com>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: "Jun'ichi Nomura" <j-nomura@ce.jp.nec.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: <stable@kernel.org> [2.6.37, 2.6.36]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Hugh Dickins and committed by
Linus Torvalds
1ce82b69 2919bfd0

+19 -29
+19 -29
mm/migrate.c
··· 622 622 int *result = NULL; 623 623 struct page *newpage = get_new_page(page, private, &result); 624 624 int remap_swapcache = 1; 625 - int rcu_locked = 0; 626 625 int charge = 0; 627 626 struct mem_cgroup *mem = NULL; 628 627 struct anon_vma *anon_vma = NULL; ··· 693 694 /* 694 695 * By try_to_unmap(), page->mapcount goes down to 0 here. In this case, 695 696 * we cannot notice that anon_vma is freed while we migrates a page. 696 - * This rcu_read_lock() delays freeing anon_vma pointer until the end 697 + * This get_anon_vma() delays freeing anon_vma pointer until the end 697 698 * of migration. File cache pages are no problem because of page_lock() 698 699 * File Caches may use write_page() or lock_page() in migration, then, 699 700 * just care Anon page here. 700 701 */ 701 702 if (PageAnon(page)) { 702 - rcu_read_lock(); 703 - rcu_locked = 1; 704 - 705 - /* Determine how to safely use anon_vma */ 706 - if (!page_mapped(page)) { 707 - if (!PageSwapCache(page)) 708 - goto rcu_unlock; 709 - 703 + /* 704 + * Only page_lock_anon_vma() understands the subtleties of 705 + * getting a hold on an anon_vma from outside one of its mms. 706 + */ 707 + anon_vma = page_lock_anon_vma(page); 708 + if (anon_vma) { 709 + /* 710 + * Take a reference count on the anon_vma if the 711 + * page is mapped so that it is guaranteed to 712 + * exist when the page is remapped later 713 + */ 714 + get_anon_vma(anon_vma); 715 + page_unlock_anon_vma(anon_vma); 716 + } else if (PageSwapCache(page)) { 710 717 /* 711 718 * We cannot be sure that the anon_vma of an unmapped 712 719 * swapcache page is safe to use because we don't ··· 727 722 */ 728 723 remap_swapcache = 0; 729 724 } else { 730 - /* 731 - * Take a reference count on the anon_vma if the 732 - * page is mapped so that it is guaranteed to 733 - * exist when the page is remapped later 734 - */ 735 - anon_vma = page_anon_vma(page); 736 - get_anon_vma(anon_vma); 725 + goto uncharge; 737 726 } 738 727 } 739 728 ··· 744 745 * free the metadata, so the page can be freed. 745 746 */ 746 747 if (!page->mapping) { 747 - if (!PageAnon(page) && page_has_private(page)) { 748 - /* 749 - * Go direct to try_to_free_buffers() here because 750 - * a) that's what try_to_release_page() would do anyway 751 - * b) we may be under rcu_read_lock() here, so we can't 752 - * use GFP_KERNEL which is what try_to_release_page() 753 - * needs to be effective. 754 - */ 748 + VM_BUG_ON(PageAnon(page)); 749 + if (page_has_private(page)) { 755 750 try_to_free_buffers(page); 756 - goto rcu_unlock; 751 + goto uncharge; 757 752 } 758 753 goto skip_unmap; 759 754 } ··· 761 768 762 769 if (rc && remap_swapcache) 763 770 remove_migration_ptes(page, page); 764 - rcu_unlock: 765 771 766 772 /* Drop an anon_vma reference if we took one */ 767 773 if (anon_vma) 768 774 drop_anon_vma(anon_vma); 769 775 770 - if (rcu_locked) 771 - rcu_read_unlock(); 772 776 uncharge: 773 777 if (!charge) 774 778 mem_cgroup_end_migration(mem, page, newpage);