--- mm/migrate.c | 15 +++++++++------ mm/rmap.c | 32 +++++++++++++++++++------------- 2 files changed, 28 insertions(+), 19 deletions(-) Index: linux-2.6/mm/rmap.c =================================================================== --- linux-2.6.orig/mm/rmap.c 2008-03-22 00:14:54.000000000 -0700 +++ linux-2.6/mm/rmap.c 2008-03-22 00:26:17.000000000 -0700 @@ -154,11 +154,11 @@ void __init anon_vma_init(void) /* * Getting a lock on a stable anon_vma from a page off the LRU is - * tricky: page_lock_anon_vma rely on RCU to guard against the races. + * tricky: page_lock_anon_vma relies on RCU to guard against the races. */ -static struct anon_vma *page_lock_anon_vma(struct page *page) +struct anon_vma *page_lock_anon_vma(struct page *page) { - struct anon_vma *anon_vma; + struct anon_vma *anon_vma = NULL; unsigned long anon_mapping; rcu_read_lock(); @@ -170,16 +170,22 @@ static struct anon_vma *page_lock_anon_v anon_vma = (struct anon_vma *) (anon_mapping - PAGE_MAPPING_ANON); spin_lock(&anon_vma->lock); - return anon_vma; + if (list_empty(&anon_vma->head)) { + /* + * List is empty we got the lock after unlink_vma() removed + * the last reference. The anon_vma is going away. So + * drop the lock and return NULL. + */ + spin_unlock(&anon_vma->lock); + anon_vma = NULL; + } + /* + * Success. The vma list is not empty and we hold the lock. That + * means that the vma cannot be freed until the lock is dropped. + */ out: rcu_read_unlock(); - return NULL; -} - -static void page_unlock_anon_vma(struct anon_vma *anon_vma) -{ - spin_unlock(&anon_vma->lock); - rcu_read_unlock(); + return anon_vma; } /* @@ -328,7 +334,7 @@ static int page_referenced_anon(struct p break; } - page_unlock_anon_vma(anon_vma); + anon_vma_unlock(vma); return referenced; } @@ -888,7 +894,7 @@ static int try_to_unmap_anon(struct page break; } - page_unlock_anon_vma(anon_vma); + anon_vma_unlock(vma); return ret; } Index: linux-2.6/mm/migrate.c =================================================================== --- linux-2.6.orig/mm/migrate.c 2008-03-22 00:26:40.000000000 -0700 +++ linux-2.6/mm/migrate.c 2008-03-22 00:44:39.000000000 -0700 @@ -625,6 +625,7 @@ static int unmap_and_move(new_page_t get struct page *newpage = get_new_page(page, private, &result); int rcu_locked = 0; int charge = 0; + struct vm_area_struct *fake_vma = NULL; if (!newpage) return -ENOMEM; @@ -654,8 +655,10 @@ static int unmap_and_move(new_page_t get * just care Anon page here. */ if (PageAnon(page)) { - rcu_read_lock(); - rcu_locked = 1; + fake_vma = vma_alloc(); + fake_vma->mm = NULL; + fake_vma->anon_vma = page->mapping - ANON_PAGE; + anon_vma_link(fake_vma); } /* @@ -681,7 +684,7 @@ static int unmap_and_move(new_page_t get */ try_to_free_buffers(page); } - goto rcu_unlock; + goto unlink_unlock; } charge = mem_cgroup_prepare_migration(page); @@ -697,9 +700,9 @@ static int unmap_and_move(new_page_t get mem_cgroup_end_migration(page); } else if (charge) mem_cgroup_end_migration(newpage); -rcu_unlock: - if (rcu_locked) - rcu_read_unlock(); +unlink_unlock: + if (fake_vma) + anon_vma_unlink(fake_vma); unlock: