Add remove_migration_ptes() 1. Modify page_check_address to support SWP_TYPE_MIGRATION 2. Add functions to scan the anon vma and restore the ptes. Signed-off-by: Christoph Lameter Index: linux-2.6.16-mm2/mm/rmap.c =================================================================== --- linux-2.6.16-mm2.orig/mm/rmap.c 2006-03-31 09:25:12.000000000 -0800 +++ linux-2.6.16-mm2/mm/rmap.c 2006-03-31 09:29:20.000000000 -0800 @@ -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,81 @@ 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); + pte = *ptep; /* Make a quick check before getting the lock */ - if (!pte_present(*pte)) { - pte_unmap(pte); + if (pte_none(pte) || pte_file(pte)) { + 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; + 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 (swp_type(entry) == SWP_TYPE_MIGRATION && + swp_offset(entry) == page_to_pfn(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; + + 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. + */ +void remove_migration_ptes(struct page *page, struct page *newpage) +{ + struct anon_vma *anon_vma; + struct vm_area_struct *vma; + + if (!PageAnon(newpage)) + return; + + anon_vma = page_lock_anon_vma(newpage); + BUG_ON(!anon_vma); + + 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. Index: linux-2.6.16-mm2/include/linux/rmap.h =================================================================== --- linux-2.6.16-mm2.orig/include/linux/rmap.h 2006-03-19 21:53:29.000000000 -0800 +++ linux-2.6.16-mm2/include/linux/rmap.h 2006-03-31 09:25:15.000000000 -0800 @@ -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)