summaryrefslogtreecommitdiff
path: root/mm/migrate.c
diff options
context:
space:
mode:
authorAndrea Arcangeli <aarcange@redhat.com>2010-05-21 01:13:49 +0200
committerAndrea Arcangeli <aarcange@redhat.com>2010-05-20 23:16:46 +0000
commited53cc3f6b50b0501a6fb8c226a3caf3667cd253 (patch)
tree6918931fda6d07fac1c0db19aa8ba4fa5f6cca2f /mm/migrate.c
parent09b1938202584797d032036d98ff706c767a03c6 (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.c8
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: