page_lock_anon_vma: fix race and add additional parameter An anon vma may be removed after the check for page_mapped. In that case we lock a vma that is then freed during rcu freeing. So add a check after we have acquired the spinlock if the page is still mapped. If the mapping has vanished then the anon_vma may also have been removed. We better unlock and fail in that case. This also adds a parameter that specifies if this mapping check should be done at all. A caller may have other means of insuring that mapping does not vanish. In particular the page migration code keeps the page lock while modifying the mappings and then calls page_lock_anon_vma via remove_from_swap to restore mappings. Signed-off-by: Christoph Lameter Index: linux-2.6.16-rc4/mm/rmap.c =================================================================== --- linux-2.6.16-rc4.orig/mm/rmap.c 2006-02-17 14:23:45.000000000 -0800 +++ linux-2.6.16-rc4/mm/rmap.c 2006-02-25 22:17:21.000000000 -0800 @@ -187,7 +187,7 @@ 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. */ -static struct anon_vma *page_lock_anon_vma(struct page *page) +static struct anon_vma *page_lock_anon_vma(struct page *page, int check_mapped) { struct anon_vma *anon_vma = NULL; unsigned long anon_mapping; @@ -196,11 +196,21 @@ static struct anon_vma *page_lock_anon_v anon_mapping = (unsigned long) page->mapping; if (!(anon_mapping & PAGE_MAPPING_ANON)) goto out; - if (!page_mapped(page)) + if (check_mapped && !page_mapped(page)) goto out; anon_vma = (struct anon_vma *) (anon_mapping - PAGE_MAPPING_ANON); spin_lock(&anon_vma->lock); + + if (check_mapped && !page_mapped(page)) { + /* + * The page vanished from any mapping and the anon_vma may + * have gone with it. The only hold of life of the anon_vma + * may be the rcu lock. Unlock and fail. + */ + spin_unlock(&anon_vma->lock); + anon_vma = NULL; + } out: rcu_read_unlock(); return anon_vma; @@ -222,7 +232,7 @@ void remove_from_swap(struct page *page) if (!PageAnon(page) || !PageSwapCache(page)) return; - anon_vma = page_lock_anon_vma(page); + anon_vma = page_lock_anon_vma(page, 0); if (!anon_vma) return; @@ -359,7 +369,7 @@ static int page_referenced_anon(struct p struct vm_area_struct *vma; int referenced = 0; - anon_vma = page_lock_anon_vma(page); + anon_vma = page_lock_anon_vma(page, 1); if (!anon_vma) return referenced; @@ -737,7 +747,7 @@ static int try_to_unmap_anon(struct page struct vm_area_struct *vma; int ret = SWAP_AGAIN; - anon_vma = page_lock_anon_vma(page); + anon_vma = page_lock_anon_vma(page, 1); if (!anon_vma) return ret;