X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=mm%2Frmap.c;h=89770bd25f31be71877813a292793fcc62e01116;hb=ca775c629a366ded01aae69da8410f70aaf85de1;hp=884d6d1928bce0b438ec7d43d7b33918e690be9c;hpb=1da177e4c3f41524e886b7f1b8a0c1fc7321cac2;p=powerpc.git diff --git a/mm/rmap.c b/mm/rmap.c index 884d6d1928..89770bd25f 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -242,6 +242,42 @@ unsigned long page_address_in_vma(struct page *page, struct vm_area_struct *vma) return vma_address(page, vma); } +/* + * Check that @page is mapped at @address into @mm. + * + * On success returns with mapped pte and locked mm->page_table_lock. + */ +static pte_t *page_check_address(struct page *page, struct mm_struct *mm, + unsigned long address) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + /* + * We need the page_table_lock to protect us from page faults, + * munmap, fork, etc... + */ + spin_lock(&mm->page_table_lock); + pgd = pgd_offset(mm, address); + if (likely(pgd_present(*pgd))) { + pud = pud_offset(pgd, address); + if (likely(pud_present(*pud))) { + pmd = pmd_offset(pud, address); + if (likely(pmd_present(*pmd))) { + pte = pte_offset_map(pmd, address); + if (likely(pte_present(*pte) && + page_to_pfn(page) == pte_pfn(*pte))) + return pte; + pte_unmap(pte); + } + } + } + spin_unlock(&mm->page_table_lock); + return ERR_PTR(-ENOENT); +} + /* * Subfunctions of page_referenced: page_referenced_one called * repeatedly from either page_referenced_anon or page_referenced_file. @@ -251,9 +287,6 @@ static int page_referenced_one(struct page *page, { struct mm_struct *mm = vma->vm_mm; unsigned long address; - pgd_t *pgd; - pud_t *pud; - pmd_t *pmd; pte_t *pte; int referenced = 0; @@ -263,39 +296,18 @@ static int page_referenced_one(struct page *page, if (address == -EFAULT) goto out; - spin_lock(&mm->page_table_lock); - - pgd = pgd_offset(mm, address); - if (!pgd_present(*pgd)) - goto out_unlock; - - pud = pud_offset(pgd, address); - if (!pud_present(*pud)) - goto out_unlock; - - pmd = pmd_offset(pud, address); - if (!pmd_present(*pmd)) - goto out_unlock; - - pte = pte_offset_map(pmd, address); - if (!pte_present(*pte)) - goto out_unmap; - - if (page_to_pfn(page) != pte_pfn(*pte)) - goto out_unmap; - - if (ptep_clear_flush_young(vma, address, pte)) - referenced++; - - if (mm != current->mm && !ignore_token && has_swap_token(mm)) - referenced++; + pte = page_check_address(page, mm, address); + if (!IS_ERR(pte)) { + if (ptep_clear_flush_young(vma, address, pte)) + referenced++; - (*mapcount)--; + if (mm != current->mm && !ignore_token && has_swap_token(mm)) + referenced++; -out_unmap: - pte_unmap(pte); -out_unlock: - spin_unlock(&mm->page_table_lock); + (*mapcount)--; + pte_unmap(pte); + spin_unlock(&mm->page_table_lock); + } out: return referenced; } @@ -502,9 +514,6 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma) { struct mm_struct *mm = vma->vm_mm; unsigned long address; - pgd_t *pgd; - pud_t *pud; - pmd_t *pmd; pte_t *pte; pte_t pteval; int ret = SWAP_AGAIN; @@ -515,30 +524,9 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma) if (address == -EFAULT) goto out; - /* - * We need the page_table_lock to protect us from page faults, - * munmap, fork, etc... - */ - spin_lock(&mm->page_table_lock); - - pgd = pgd_offset(mm, address); - if (!pgd_present(*pgd)) - goto out_unlock; - - pud = pud_offset(pgd, address); - if (!pud_present(*pud)) - goto out_unlock; - - pmd = pmd_offset(pud, address); - if (!pmd_present(*pmd)) - goto out_unlock; - - pte = pte_offset_map(pmd, address); - if (!pte_present(*pte)) - goto out_unmap; - - if (page_to_pfn(page) != pte_pfn(*pte)) - goto out_unmap; + pte = page_check_address(page, mm, address); + if (IS_ERR(pte)) + goto out; /* * If the page is mlock()d, we cannot swap it out. @@ -551,27 +539,6 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma) goto out_unmap; } - /* - * Don't pull an anonymous page out from under get_user_pages. - * GUP carefully breaks COW and raises page count (while holding - * page_table_lock, as we have here) to make sure that the page - * cannot be freed. If we unmap that page here, a user write - * access to the virtual address will bring back the page, but - * its raised count will (ironically) be taken to mean it's not - * an exclusive swap page, do_wp_page will replace it by a copy - * page, and the user never get to see the data GUP was holding - * the original page for. - * - * This test is also useful for when swapoff (unuse_process) has - * to drop page lock: its reference to the page stops existing - * ptes from being unmapped, so swapoff can make progress. - */ - if (PageSwapCache(page) && - page_count(page) != page_mapcount(page) + 2) { - ret = SWAP_FAIL; - goto out_unmap; - } - /* Nuke the page table entry. */ flush_cache_page(vma, address, page_to_pfn(page)); pteval = ptep_clear_flush(vma, address, pte); @@ -598,13 +565,12 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma) dec_mm_counter(mm, anon_rss); } - inc_mm_counter(mm, rss); + dec_mm_counter(mm, rss); page_remove_rmap(page); page_cache_release(page); out_unmap: pte_unmap(pte); -out_unlock: spin_unlock(&mm->page_table_lock); out: return ret; @@ -639,7 +605,7 @@ static void try_to_unmap_cluster(unsigned long cursor, pgd_t *pgd; pud_t *pud; pmd_t *pmd; - pte_t *pte; + pte_t *pte, *original_pte; pte_t pteval; struct page *page; unsigned long address; @@ -671,7 +637,7 @@ static void try_to_unmap_cluster(unsigned long cursor, if (!pmd_present(*pmd)) goto out_unlock; - for (pte = pte_offset_map(pmd, address); + for (original_pte = pte = pte_offset_map(pmd, address); address < end; pte++, address += PAGE_SIZE) { if (!pte_present(*pte)) @@ -707,8 +673,7 @@ static void try_to_unmap_cluster(unsigned long cursor, (*mapcount)--; } - pte_unmap(pte); - + pte_unmap(original_pte); out_unlock: spin_unlock(&mm->page_table_lock); } @@ -860,3 +825,4 @@ int try_to_unmap(struct page *page) ret = SWAP_SUCCESS; return ret; } +