diff options
author | Hugh Dickins <hughd@google.com> | 2011-01-13 18:47:30 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-13 20:32:49 -0500 |
commit | 1ce82b69e96c838d007f316b8347b911fdfa9842 (patch) | |
tree | be34e7c88a2544e620638834c8120b14b277d64a /mm | |
parent | 2919bfd0758257c469abef8c26c3e516bbebb851 (diff) |
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>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/migrate.c | 48 |
1 files changed, 19 insertions, 29 deletions
diff --git a/mm/migrate.c b/mm/migrate.c index 89a6bc8cd307..a20cf12edede 100644 --- a/mm/migrate.c +++ b/mm/migrate.c | |||
@@ -622,7 +622,6 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, | |||
622 | int *result = NULL; | 622 | int *result = NULL; |
623 | struct page *newpage = get_new_page(page, private, &result); | 623 | struct page *newpage = get_new_page(page, private, &result); |
624 | int remap_swapcache = 1; | 624 | int remap_swapcache = 1; |
625 | int rcu_locked = 0; | ||
626 | int charge = 0; | 625 | int charge = 0; |
627 | struct mem_cgroup *mem = NULL; | 626 | struct mem_cgroup *mem = NULL; |
628 | struct anon_vma *anon_vma = NULL; | 627 | struct anon_vma *anon_vma = NULL; |
@@ -694,20 +693,26 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, | |||
694 | /* | 693 | /* |
695 | * By try_to_unmap(), page->mapcount goes down to 0 here. In this case, | 694 | * By try_to_unmap(), page->mapcount goes down to 0 here. In this case, |
696 | * we cannot notice that anon_vma is freed while we migrates a page. | 695 | * we cannot notice that anon_vma is freed while we migrates a page. |
697 | * This rcu_read_lock() delays freeing anon_vma pointer until the end | 696 | * This get_anon_vma() delays freeing anon_vma pointer until the end |
698 | * of migration. File cache pages are no problem because of page_lock() | 697 | * of migration. File cache pages are no problem because of page_lock() |
699 | * File Caches may use write_page() or lock_page() in migration, then, | 698 | * File Caches may use write_page() or lock_page() in migration, then, |
700 | * just care Anon page here. | 699 | * just care Anon page here. |
701 | */ | 700 | */ |
702 | if (PageAnon(page)) { | 701 | if (PageAnon(page)) { |
703 | rcu_read_lock(); | 702 | /* |
704 | rcu_locked = 1; | 703 | * Only page_lock_anon_vma() understands the subtleties of |
705 | 704 | * getting a hold on an anon_vma from outside one of its mms. | |
706 | /* Determine how to safely use anon_vma */ | 705 | */ |
707 | if (!page_mapped(page)) { | 706 | anon_vma = page_lock_anon_vma(page); |
708 | if (!PageSwapCache(page)) | 707 | if (anon_vma) { |
709 | goto rcu_unlock; | 708 | /* |
710 | 709 | * Take a reference count on the anon_vma if the | |
710 | * page is mapped so that it is guaranteed to | ||
711 | * exist when the page is remapped later | ||
712 | */ | ||
713 | get_anon_vma(anon_vma); | ||
714 | page_unlock_anon_vma(anon_vma); | ||
715 | } else if (PageSwapCache(page)) { | ||
711 | /* | 716 | /* |
712 | * We cannot be sure that the anon_vma of an unmapped | 717 | * We cannot be sure that the anon_vma of an unmapped |
713 | * swapcache page is safe to use because we don't | 718 | * swapcache page is safe to use because we don't |
@@ -722,13 +727,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, | |||
722 | */ | 727 | */ |
723 | remap_swapcache = 0; | 728 | remap_swapcache = 0; |
724 | } else { | 729 | } else { |
725 | /* | 730 | goto uncharge; |
726 | * Take a reference count on the anon_vma if the | ||
727 | * page is mapped so that it is guaranteed to | ||
728 | * exist when the page is remapped later | ||
729 | */ | ||
730 | anon_vma = page_anon_vma(page); | ||
731 | get_anon_vma(anon_vma); | ||
732 | } | 731 | } |
733 | } | 732 | } |
734 | 733 | ||
@@ -745,16 +744,10 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, | |||
745 | * free the metadata, so the page can be freed. | 744 | * free the metadata, so the page can be freed. |
746 | */ | 745 | */ |
747 | if (!page->mapping) { | 746 | if (!page->mapping) { |
748 | if (!PageAnon(page) && page_has_private(page)) { | 747 | VM_BUG_ON(PageAnon(page)); |
749 | /* | 748 | if (page_has_private(page)) { |
750 | * Go direct to try_to_free_buffers() here because | ||
751 | * a) that's what try_to_release_page() would do anyway | ||
752 | * b) we may be under rcu_read_lock() here, so we can't | ||
753 | * use GFP_KERNEL which is what try_to_release_page() | ||
754 | * needs to be effective. | ||
755 | */ | ||
756 | try_to_free_buffers(page); | 749 | try_to_free_buffers(page); |
757 | goto rcu_unlock; | 750 | goto uncharge; |
758 | } | 751 | } |
759 | goto skip_unmap; | 752 | goto skip_unmap; |
760 | } | 753 | } |
@@ -768,14 +761,11 @@ skip_unmap: | |||
768 | 761 | ||
769 | if (rc && remap_swapcache) | 762 | if (rc && remap_swapcache) |
770 | remove_migration_ptes(page, page); | 763 | remove_migration_ptes(page, page); |
771 | rcu_unlock: | ||
772 | 764 | ||
773 | /* Drop an anon_vma reference if we took one */ | 765 | /* Drop an anon_vma reference if we took one */ |
774 | if (anon_vma) | 766 | if (anon_vma) |
775 | drop_anon_vma(anon_vma); | 767 | drop_anon_vma(anon_vma); |
776 | 768 | ||
777 | if (rcu_locked) | ||
778 | rcu_read_unlock(); | ||
779 | uncharge: | 769 | uncharge: |
780 | if (!charge) | 770 | if (!charge) |
781 | mem_cgroup_end_migration(mem, page, newpage); | 771 | mem_cgroup_end_migration(mem, page, newpage); |