diff options
author | Andrea Arcangeli <aarcange@redhat.com> | 2010-05-21 01:13:49 +0200 |
---|---|---|
committer | Andrea Arcangeli <aarcange@redhat.com> | 2010-05-20 23:16:46 +0000 |
commit | ed53cc3f6b50b0501a6fb8c226a3caf3667cd253 (patch) | |
tree | 6918931fda6d07fac1c0db19aa8ba4fa5f6cca2f /mm/migrate.c | |
parent | 09b1938202584797d032036d98ff706c767a03c6 (diff) |
Take a reference to the anon_vma before migrating
rmap_walk_anon() does not use page_lock_anon_vma() for looking up and
locking an anon_vma and it does not appear to have sufficient locking to
ensure the anon_vma does not disappear from under it.
This patch copies an approach used by KSM to take a reference on the
anon_vma while pages are being migrated. This should prevent rmap_walk()
running into nasty surprises later because anon_vma has been freed.
Signed-off-by: Mel Gorman <mel@csn.ul.ie>
Acked-by: Rik van Riel <riel@redhat.com>
Diffstat (limited to 'mm/migrate.c')
-rw-r--r-- | mm/migrate.c | 8 |
1 files changed, 8 insertions, 0 deletions
diff --git a/mm/migrate.c b/mm/migrate.c index a21d62925e22..4be196020790 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -555,6 +555,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, int rcu_locked = 0; int charge = 0; struct mem_cgroup *mem = NULL; + struct anon_vma *anon_vma = NULL; if (!newpage) return -ENOMEM; @@ -614,6 +615,8 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, if (PageAnon(page)) { rcu_read_lock(); rcu_locked = 1; + anon_vma = page_anon_vma(page); + get_anon_vma(anon_vma); } /* @@ -653,6 +656,11 @@ skip_unmap: if (rc) remove_migration_ptes(page, page); rcu_unlock: + + /* Drop an anon_vma reference if we took one */ + if (anon_vma) + drop_anon_vma(anon_vma); + if (rcu_locked) rcu_read_unlock(); uncharge: |