Add ability to remove migration ptes. 1. Modify page_check_address to support matching on migration ptes. 2. Add functions to scan the anon vma reverse maps and replace migration entries with regular ptes. Signed-off-by: Christoph Lameter Index: linux-2.6.17-rc1-mm2/mm/rmap.c =================================================================== --- linux-2.6.17-rc1-mm2.orig/mm/rmap.c 2006-04-13 15:18:45.000000000 -0700 +++ linux-2.6.17-rc1-mm2/mm/rmap.c 2006-04-13 15:18:58.000000000 -0700 @@ -291,7 +291,7 @@ pte_t *page_check_address(struct page *p pgd_t *pgd; pud_t *pud; pmd_t *pmd; - pte_t *pte; + pte_t *ptep, pte; spinlock_t *ptl; pgd = pgd_offset(mm, address); @@ -306,23 +306,92 @@ pte_t *page_check_address(struct page *p if (!pmd_present(*pmd)) return NULL; - pte = pte_offset_map(pmd, address); + ptep = pte_offset_map(pmd, address); /* Make a quick check before getting the lock */ - if (!pte_present(*pte)) { - pte_unmap(pte); + if (pte_none(*ptep) || pte_file(*ptep)) { + pte_unmap(ptep); return NULL; } ptl = pte_lockptr(mm, pmd); spin_lock(ptl); - if (pte_present(*pte) && page_to_pfn(page) == pte_pfn(*pte)) { - *ptlp = ptl; - return pte; + pte = *ptep; + if (pte_present(pte)) { + if (page_to_pfn(page) == pte_pfn(pte)) { + *ptlp = ptl; + return ptep; + } + } else { + /* Could still be a migration entry pointing to the page */ + swp_entry_t entry = pte_to_swp_entry(pte); + + if (is_migration_entry(entry) && + migration_entry_to_page(entry) == page) { + *ptlp = ptl; + return ptep; + } } pte_unmap_unlock(pte, ptl); return NULL; } +#ifdef CONFIG_MIGRATION +/* + * Restore a potential migration pte to a working pte entry for + * anonymous pages. + */ +static void remove_migration_pte(struct vm_area_struct *vma, unsigned long addr, + struct page *old, struct page *new) +{ + struct mm_struct *mm = vma->vm_mm; + pte_t *ptep; + spinlock_t *ptl; + + ptep = page_check_address(old, mm, addr, &ptl); + if (!ptep) + return; + + if (!pte_present(*ptep)) { + inc_mm_counter(vma->vm_mm, anon_rss); + get_page(new); + set_pte_at(mm, addr, ptep, pte_mkold(mk_pte(new, vma->vm_page_prot))); + page_add_anon_rmap(new, vma, addr); + } + spin_unlock(ptl); +} + +/* + * Get rid of all migration entries and replace them by + * references to the indicated page. + * + * Must hold mmap_sem lock on at least one of the vmas containing + * the page so that the anon_vma cannot vanish. + */ +void remove_migration_ptes(struct page *page, struct page *newpage) +{ + struct anon_vma *anon_vma; + struct vm_area_struct *vma; + unsigned long mapping; + + mapping = (unsigned long)newpage->mapping; + + if (!mapping || (mapping & PAGE_MAPPING_ANON) == 0) + return; + + /* + * We hold the mmap_sem lock. So no need to call page_lock_anon_vma. + */ + anon_vma = (struct anon_vma *) (mapping - PAGE_MAPPING_ANON); + spin_lock(&anon_vma->lock); + + list_for_each_entry(vma, &anon_vma->head, anon_vma_node) + remove_migration_pte(vma, page_address_in_vma(newpage, vma), + page, newpage); + + spin_unlock(&anon_vma->lock); +} +#endif + /* * Subfunctions of page_referenced: page_referenced_one called * repeatedly from either page_referenced_anon or page_referenced_file. @@ -595,6 +664,8 @@ static int try_to_unmap_one(struct page if (!pte) goto out; + BUG_ON(!pte_present(*pte)); + /* * If the page is mlock()d, we cannot swap it out. * If it's recently referenced (perhaps page_referenced Index: linux-2.6.17-rc1-mm2/include/linux/rmap.h =================================================================== --- linux-2.6.17-rc1-mm2.orig/include/linux/rmap.h 2006-04-02 20:22:10.000000000 -0700 +++ linux-2.6.17-rc1-mm2/include/linux/rmap.h 2006-04-13 15:18:58.000000000 -0700 @@ -105,6 +105,11 @@ pte_t *page_check_address(struct page *, */ unsigned long page_address_in_vma(struct page *, struct vm_area_struct *); +/* + * Used by page migration to restore ptes of anonymous pages + */ +void remove_migration_ptes(struct page *page, struct page *newpage); + #else /* !CONFIG_MMU */ #define anon_vma_init() do {} while (0)