diff options
Diffstat (limited to 'fs/proc/task_mmu.c')
-rw-r--r-- | fs/proc/task_mmu.c | 39 |
1 files changed, 30 insertions, 9 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index f0df3109343d..ab8ccc9d14ff 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -553,24 +553,45 @@ static u64 swap_pte_to_pagemap_entry(pte_t pte) | |||
553 | return swp_type(e) | (swp_offset(e) << MAX_SWAPFILES_SHIFT); | 553 | return swp_type(e) | (swp_offset(e) << MAX_SWAPFILES_SHIFT); |
554 | } | 554 | } |
555 | 555 | ||
556 | static unsigned long pte_to_pagemap_entry(pte_t pte) | ||
557 | { | ||
558 | unsigned long pme = 0; | ||
559 | if (is_swap_pte(pte)) | ||
560 | pme = PM_PFRAME(swap_pte_to_pagemap_entry(pte)) | ||
561 | | PM_PSHIFT(PAGE_SHIFT) | PM_SWAP; | ||
562 | else if (pte_present(pte)) | ||
563 | pme = PM_PFRAME(pte_pfn(pte)) | ||
564 | | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT; | ||
565 | return pme; | ||
566 | } | ||
567 | |||
556 | static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | 568 | static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, |
557 | struct mm_walk *walk) | 569 | struct mm_walk *walk) |
558 | { | 570 | { |
571 | struct vm_area_struct *vma; | ||
559 | struct pagemapread *pm = walk->private; | 572 | struct pagemapread *pm = walk->private; |
560 | pte_t *pte; | 573 | pte_t *pte; |
561 | int err = 0; | 574 | int err = 0; |
562 | 575 | ||
576 | /* find the first VMA at or above 'addr' */ | ||
577 | vma = find_vma(walk->mm, addr); | ||
563 | for (; addr != end; addr += PAGE_SIZE) { | 578 | for (; addr != end; addr += PAGE_SIZE) { |
564 | u64 pfn = PM_NOT_PRESENT; | 579 | u64 pfn = PM_NOT_PRESENT; |
565 | pte = pte_offset_map(pmd, addr); | 580 | |
566 | if (is_swap_pte(*pte)) | 581 | /* check to see if we've left 'vma' behind |
567 | pfn = PM_PFRAME(swap_pte_to_pagemap_entry(*pte)) | 582 | * and need a new, higher one */ |
568 | | PM_PSHIFT(PAGE_SHIFT) | PM_SWAP; | 583 | if (vma && (addr >= vma->vm_end)) |
569 | else if (pte_present(*pte)) | 584 | vma = find_vma(walk->mm, addr); |
570 | pfn = PM_PFRAME(pte_pfn(*pte)) | 585 | |
571 | | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT; | 586 | /* check that 'vma' actually covers this address, |
572 | /* unmap so we're not in atomic when we copy to userspace */ | 587 | * and that it isn't a huge page vma */ |
573 | pte_unmap(pte); | 588 | if (vma && (vma->vm_start <= addr) && |
589 | !is_vm_hugetlb_page(vma)) { | ||
590 | pte = pte_offset_map(pmd, addr); | ||
591 | pfn = pte_to_pagemap_entry(*pte); | ||
592 | /* unmap before userspace copy */ | ||
593 | pte_unmap(pte); | ||
594 | } | ||
574 | err = add_to_pagemap(addr, pfn, pm); | 595 | err = add_to_pagemap(addr, pfn, pm); |
575 | if (err) | 596 | if (err) |
576 | return err; | 597 | return err; |