diff options
Diffstat (limited to 'fs/proc/task_mmu.c')
-rw-r--r-- | fs/proc/task_mmu.c | 68 |
1 files changed, 47 insertions, 21 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 49958cffbd8d..9dfb5ff24209 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -527,13 +527,21 @@ struct pagemapread { | |||
527 | char __user *out, *end; | 527 | char __user *out, *end; |
528 | }; | 528 | }; |
529 | 529 | ||
530 | #define PM_ENTRY_BYTES sizeof(u64) | 530 | #define PM_ENTRY_BYTES sizeof(u64) |
531 | #define PM_RESERVED_BITS 3 | 531 | #define PM_STATUS_BITS 3 |
532 | #define PM_RESERVED_OFFSET (64 - PM_RESERVED_BITS) | 532 | #define PM_STATUS_OFFSET (64 - PM_STATUS_BITS) |
533 | #define PM_RESERVED_MASK (((1LL<<PM_RESERVED_BITS)-1) << PM_RESERVED_OFFSET) | 533 | #define PM_STATUS_MASK (((1LL << PM_STATUS_BITS) - 1) << PM_STATUS_OFFSET) |
534 | #define PM_SPECIAL(nr) (((nr) << PM_RESERVED_OFFSET) | PM_RESERVED_MASK) | 534 | #define PM_STATUS(nr) (((nr) << PM_STATUS_OFFSET) & PM_STATUS_MASK) |
535 | #define PM_NOT_PRESENT PM_SPECIAL(1LL) | 535 | #define PM_PSHIFT_BITS 6 |
536 | #define PM_SWAP PM_SPECIAL(2LL) | 536 | #define PM_PSHIFT_OFFSET (PM_STATUS_OFFSET - PM_PSHIFT_BITS) |
537 | #define PM_PSHIFT_MASK (((1LL << PM_PSHIFT_BITS) - 1) << PM_PSHIFT_OFFSET) | ||
538 | #define PM_PSHIFT(x) (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK) | ||
539 | #define PM_PFRAME_MASK ((1LL << PM_PSHIFT_OFFSET) - 1) | ||
540 | #define PM_PFRAME(x) ((x) & PM_PFRAME_MASK) | ||
541 | |||
542 | #define PM_PRESENT PM_STATUS(4LL) | ||
543 | #define PM_SWAP PM_STATUS(2LL) | ||
544 | #define PM_NOT_PRESENT PM_PSHIFT(PAGE_SHIFT) | ||
537 | #define PM_END_OF_BUFFER 1 | 545 | #define PM_END_OF_BUFFER 1 |
538 | 546 | ||
539 | static int add_to_pagemap(unsigned long addr, u64 pfn, | 547 | static int add_to_pagemap(unsigned long addr, u64 pfn, |
@@ -574,7 +582,7 @@ static int pagemap_pte_hole(unsigned long start, unsigned long end, | |||
574 | u64 swap_pte_to_pagemap_entry(pte_t pte) | 582 | u64 swap_pte_to_pagemap_entry(pte_t pte) |
575 | { | 583 | { |
576 | swp_entry_t e = pte_to_swp_entry(pte); | 584 | swp_entry_t e = pte_to_swp_entry(pte); |
577 | return PM_SWAP | swp_type(e) | (swp_offset(e) << MAX_SWAPFILES_SHIFT); | 585 | return swp_type(e) | (swp_offset(e) << MAX_SWAPFILES_SHIFT); |
578 | } | 586 | } |
579 | 587 | ||
580 | static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | 588 | static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, |
@@ -588,9 +596,11 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | |||
588 | u64 pfn = PM_NOT_PRESENT; | 596 | u64 pfn = PM_NOT_PRESENT; |
589 | pte = pte_offset_map(pmd, addr); | 597 | pte = pte_offset_map(pmd, addr); |
590 | if (is_swap_pte(*pte)) | 598 | if (is_swap_pte(*pte)) |
591 | pfn = swap_pte_to_pagemap_entry(*pte); | 599 | pfn = PM_PFRAME(swap_pte_to_pagemap_entry(*pte)) |
600 | | PM_PSHIFT(PAGE_SHIFT) | PM_SWAP; | ||
592 | else if (pte_present(*pte)) | 601 | else if (pte_present(*pte)) |
593 | pfn = pte_pfn(*pte); | 602 | pfn = PM_PFRAME(pte_pfn(*pte)) |
603 | | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT; | ||
594 | /* unmap so we're not in atomic when we copy to userspace */ | 604 | /* unmap so we're not in atomic when we copy to userspace */ |
595 | pte_unmap(pte); | 605 | pte_unmap(pte); |
596 | err = add_to_pagemap(addr, pfn, pm); | 606 | err = add_to_pagemap(addr, pfn, pm); |
@@ -611,12 +621,20 @@ static struct mm_walk pagemap_walk = { | |||
611 | /* | 621 | /* |
612 | * /proc/pid/pagemap - an array mapping virtual pages to pfns | 622 | * /proc/pid/pagemap - an array mapping virtual pages to pfns |
613 | * | 623 | * |
614 | * For each page in the address space, this file contains one 64-bit | 624 | * For each page in the address space, this file contains one 64-bit entry |
615 | * entry representing the corresponding physical page frame number | 625 | * consisting of the following: |
616 | * (PFN) if the page is present. If there is a swap entry for the | 626 | * |
617 | * physical page, then an encoding of the swap file number and the | 627 | * Bits 0-55 page frame number (PFN) if present |
618 | * page's offset into the swap file are returned. If no page is | 628 | * Bits 0-4 swap type if swapped |
619 | * present at all, PM_NOT_PRESENT is returned. This allows determining | 629 | * Bits 5-55 swap offset if swapped |
630 | * Bits 55-60 page shift (page size = 1<<page shift) | ||
631 | * Bit 61 reserved for future use | ||
632 | * Bit 62 page swapped | ||
633 | * Bit 63 page present | ||
634 | * | ||
635 | * If the page is not present but in swap, then the PFN contains an | ||
636 | * encoding of the swap file number and the page's offset into the | ||
637 | * swap. Unmapped pages return a null PFN. This allows determining | ||
620 | * precisely which pages are mapped (or in swap) and comparing mapped | 638 | * precisely which pages are mapped (or in swap) and comparing mapped |
621 | * pages between processes. | 639 | * pages between processes. |
622 | * | 640 | * |
@@ -640,17 +658,17 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, | |||
640 | 658 | ||
641 | ret = -EACCES; | 659 | ret = -EACCES; |
642 | if (!ptrace_may_attach(task)) | 660 | if (!ptrace_may_attach(task)) |
643 | goto out; | 661 | goto out_task; |
644 | 662 | ||
645 | ret = -EINVAL; | 663 | ret = -EINVAL; |
646 | /* file position must be aligned */ | 664 | /* file position must be aligned */ |
647 | if (*ppos % PM_ENTRY_BYTES) | 665 | if (*ppos % PM_ENTRY_BYTES) |
648 | goto out; | 666 | goto out_task; |
649 | 667 | ||
650 | ret = 0; | 668 | ret = 0; |
651 | mm = get_task_mm(task); | 669 | mm = get_task_mm(task); |
652 | if (!mm) | 670 | if (!mm) |
653 | goto out; | 671 | goto out_task; |
654 | 672 | ||
655 | ret = -ENOMEM; | 673 | ret = -ENOMEM; |
656 | uaddr = (unsigned long)buf & PAGE_MASK; | 674 | uaddr = (unsigned long)buf & PAGE_MASK; |
@@ -658,7 +676,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, | |||
658 | pagecount = (PAGE_ALIGN(uend) - uaddr) / PAGE_SIZE; | 676 | pagecount = (PAGE_ALIGN(uend) - uaddr) / PAGE_SIZE; |
659 | pages = kmalloc(pagecount * sizeof(struct page *), GFP_KERNEL); | 677 | pages = kmalloc(pagecount * sizeof(struct page *), GFP_KERNEL); |
660 | if (!pages) | 678 | if (!pages) |
661 | goto out_task; | 679 | goto out_mm; |
662 | 680 | ||
663 | down_read(¤t->mm->mmap_sem); | 681 | down_read(¤t->mm->mmap_sem); |
664 | ret = get_user_pages(current, current->mm, uaddr, pagecount, | 682 | ret = get_user_pages(current, current->mm, uaddr, pagecount, |
@@ -668,6 +686,12 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, | |||
668 | if (ret < 0) | 686 | if (ret < 0) |
669 | goto out_free; | 687 | goto out_free; |
670 | 688 | ||
689 | if (ret != pagecount) { | ||
690 | pagecount = ret; | ||
691 | ret = -EFAULT; | ||
692 | goto out_pages; | ||
693 | } | ||
694 | |||
671 | pm.out = buf; | 695 | pm.out = buf; |
672 | pm.end = buf + count; | 696 | pm.end = buf + count; |
673 | 697 | ||
@@ -699,15 +723,17 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, | |||
699 | ret = pm.out - buf; | 723 | ret = pm.out - buf; |
700 | } | 724 | } |
701 | 725 | ||
726 | out_pages: | ||
702 | for (; pagecount; pagecount--) { | 727 | for (; pagecount; pagecount--) { |
703 | page = pages[pagecount-1]; | 728 | page = pages[pagecount-1]; |
704 | if (!PageReserved(page)) | 729 | if (!PageReserved(page)) |
705 | SetPageDirty(page); | 730 | SetPageDirty(page); |
706 | page_cache_release(page); | 731 | page_cache_release(page); |
707 | } | 732 | } |
708 | mmput(mm); | ||
709 | out_free: | 733 | out_free: |
710 | kfree(pages); | 734 | kfree(pages); |
735 | out_mm: | ||
736 | mmput(mm); | ||
711 | out_task: | 737 | out_task: |
712 | put_task_struct(task); | 738 | put_task_struct(task); |
713 | out: | 739 | out: |