diff options
Diffstat (limited to 'fs/proc/task_mmu.c')
-rw-r--r-- | fs/proc/task_mmu.c | 47 |
1 files changed, 42 insertions, 5 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 39d641292579..a18e065c1c3e 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/rmap.h> | 11 | #include <linux/rmap.h> |
12 | #include <linux/swap.h> | 12 | #include <linux/swap.h> |
13 | #include <linux/swapops.h> | 13 | #include <linux/swapops.h> |
14 | #include <linux/mmu_notifier.h> | ||
14 | 15 | ||
15 | #include <asm/elf.h> | 16 | #include <asm/elf.h> |
16 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
@@ -692,13 +693,32 @@ enum clear_refs_types { | |||
692 | CLEAR_REFS_ALL = 1, | 693 | CLEAR_REFS_ALL = 1, |
693 | CLEAR_REFS_ANON, | 694 | CLEAR_REFS_ANON, |
694 | CLEAR_REFS_MAPPED, | 695 | CLEAR_REFS_MAPPED, |
696 | CLEAR_REFS_SOFT_DIRTY, | ||
695 | CLEAR_REFS_LAST, | 697 | CLEAR_REFS_LAST, |
696 | }; | 698 | }; |
697 | 699 | ||
698 | struct clear_refs_private { | 700 | struct clear_refs_private { |
699 | struct vm_area_struct *vma; | 701 | struct vm_area_struct *vma; |
702 | enum clear_refs_types type; | ||
700 | }; | 703 | }; |
701 | 704 | ||
705 | static inline void clear_soft_dirty(struct vm_area_struct *vma, | ||
706 | unsigned long addr, pte_t *pte) | ||
707 | { | ||
708 | #ifdef CONFIG_MEM_SOFT_DIRTY | ||
709 | /* | ||
710 | * The soft-dirty tracker uses #PF-s to catch writes | ||
711 | * to pages, so write-protect the pte as well. See the | ||
712 | * Documentation/vm/soft-dirty.txt for full description | ||
713 | * of how soft-dirty works. | ||
714 | */ | ||
715 | pte_t ptent = *pte; | ||
716 | ptent = pte_wrprotect(ptent); | ||
717 | ptent = pte_clear_flags(ptent, _PAGE_SOFT_DIRTY); | ||
718 | set_pte_at(vma->vm_mm, addr, pte, ptent); | ||
719 | #endif | ||
720 | } | ||
721 | |||
702 | static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, | 722 | static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, |
703 | unsigned long end, struct mm_walk *walk) | 723 | unsigned long end, struct mm_walk *walk) |
704 | { | 724 | { |
@@ -718,6 +738,11 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, | |||
718 | if (!pte_present(ptent)) | 738 | if (!pte_present(ptent)) |
719 | continue; | 739 | continue; |
720 | 740 | ||
741 | if (cp->type == CLEAR_REFS_SOFT_DIRTY) { | ||
742 | clear_soft_dirty(vma, addr, pte); | ||
743 | continue; | ||
744 | } | ||
745 | |||
721 | page = vm_normal_page(vma, addr, ptent); | 746 | page = vm_normal_page(vma, addr, ptent); |
722 | if (!page) | 747 | if (!page) |
723 | continue; | 748 | continue; |
@@ -759,6 +784,7 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, | |||
759 | mm = get_task_mm(task); | 784 | mm = get_task_mm(task); |
760 | if (mm) { | 785 | if (mm) { |
761 | struct clear_refs_private cp = { | 786 | struct clear_refs_private cp = { |
787 | .type = type, | ||
762 | }; | 788 | }; |
763 | struct mm_walk clear_refs_walk = { | 789 | struct mm_walk clear_refs_walk = { |
764 | .pmd_entry = clear_refs_pte_range, | 790 | .pmd_entry = clear_refs_pte_range, |
@@ -766,6 +792,8 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, | |||
766 | .private = &cp, | 792 | .private = &cp, |
767 | }; | 793 | }; |
768 | down_read(&mm->mmap_sem); | 794 | down_read(&mm->mmap_sem); |
795 | if (type == CLEAR_REFS_SOFT_DIRTY) | ||
796 | mmu_notifier_invalidate_range_start(mm, 0, -1); | ||
769 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | 797 | for (vma = mm->mmap; vma; vma = vma->vm_next) { |
770 | cp.vma = vma; | 798 | cp.vma = vma; |
771 | if (is_vm_hugetlb_page(vma)) | 799 | if (is_vm_hugetlb_page(vma)) |
@@ -786,6 +814,8 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, | |||
786 | walk_page_range(vma->vm_start, vma->vm_end, | 814 | walk_page_range(vma->vm_start, vma->vm_end, |
787 | &clear_refs_walk); | 815 | &clear_refs_walk); |
788 | } | 816 | } |
817 | if (type == CLEAR_REFS_SOFT_DIRTY) | ||
818 | mmu_notifier_invalidate_range_end(mm, 0, -1); | ||
789 | flush_tlb_mm(mm); | 819 | flush_tlb_mm(mm); |
790 | up_read(&mm->mmap_sem); | 820 | up_read(&mm->mmap_sem); |
791 | mmput(mm); | 821 | mmput(mm); |
@@ -827,6 +857,7 @@ struct pagemapread { | |||
827 | /* in "new" pagemap pshift bits are occupied with more status bits */ | 857 | /* in "new" pagemap pshift bits are occupied with more status bits */ |
828 | #define PM_STATUS2(v2, x) (__PM_PSHIFT(v2 ? x : PAGE_SHIFT)) | 858 | #define PM_STATUS2(v2, x) (__PM_PSHIFT(v2 ? x : PAGE_SHIFT)) |
829 | 859 | ||
860 | #define __PM_SOFT_DIRTY (1LL) | ||
830 | #define PM_PRESENT PM_STATUS(4LL) | 861 | #define PM_PRESENT PM_STATUS(4LL) |
831 | #define PM_SWAP PM_STATUS(2LL) | 862 | #define PM_SWAP PM_STATUS(2LL) |
832 | #define PM_FILE PM_STATUS(1LL) | 863 | #define PM_FILE PM_STATUS(1LL) |
@@ -868,6 +899,7 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | |||
868 | { | 899 | { |
869 | u64 frame, flags; | 900 | u64 frame, flags; |
870 | struct page *page = NULL; | 901 | struct page *page = NULL; |
902 | int flags2 = 0; | ||
871 | 903 | ||
872 | if (pte_present(pte)) { | 904 | if (pte_present(pte)) { |
873 | frame = pte_pfn(pte); | 905 | frame = pte_pfn(pte); |
@@ -888,13 +920,15 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | |||
888 | 920 | ||
889 | if (page && !PageAnon(page)) | 921 | if (page && !PageAnon(page)) |
890 | flags |= PM_FILE; | 922 | flags |= PM_FILE; |
923 | if (pte_soft_dirty(pte)) | ||
924 | flags2 |= __PM_SOFT_DIRTY; | ||
891 | 925 | ||
892 | *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, 0) | flags); | 926 | *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, flags2) | flags); |
893 | } | 927 | } |
894 | 928 | ||
895 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 929 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
896 | static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | 930 | static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, |
897 | pmd_t pmd, int offset) | 931 | pmd_t pmd, int offset, int pmd_flags2) |
898 | { | 932 | { |
899 | /* | 933 | /* |
900 | * Currently pmd for thp is always present because thp can not be | 934 | * Currently pmd for thp is always present because thp can not be |
@@ -903,13 +937,13 @@ static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *p | |||
903 | */ | 937 | */ |
904 | if (pmd_present(pmd)) | 938 | if (pmd_present(pmd)) |
905 | *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) | 939 | *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) |
906 | | PM_STATUS2(pm->v2, 0) | PM_PRESENT); | 940 | | PM_STATUS2(pm->v2, pmd_flags2) | PM_PRESENT); |
907 | else | 941 | else |
908 | *pme = make_pme(PM_NOT_PRESENT(pm->v2)); | 942 | *pme = make_pme(PM_NOT_PRESENT(pm->v2)); |
909 | } | 943 | } |
910 | #else | 944 | #else |
911 | static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | 945 | static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, |
912 | pmd_t pmd, int offset) | 946 | pmd_t pmd, int offset, int pmd_flags2) |
913 | { | 947 | { |
914 | } | 948 | } |
915 | #endif | 949 | #endif |
@@ -926,12 +960,15 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | |||
926 | /* find the first VMA at or above 'addr' */ | 960 | /* find the first VMA at or above 'addr' */ |
927 | vma = find_vma(walk->mm, addr); | 961 | vma = find_vma(walk->mm, addr); |
928 | if (vma && pmd_trans_huge_lock(pmd, vma) == 1) { | 962 | if (vma && pmd_trans_huge_lock(pmd, vma) == 1) { |
963 | int pmd_flags2; | ||
964 | |||
965 | pmd_flags2 = (pmd_soft_dirty(*pmd) ? __PM_SOFT_DIRTY : 0); | ||
929 | for (; addr != end; addr += PAGE_SIZE) { | 966 | for (; addr != end; addr += PAGE_SIZE) { |
930 | unsigned long offset; | 967 | unsigned long offset; |
931 | 968 | ||
932 | offset = (addr & ~PAGEMAP_WALK_MASK) >> | 969 | offset = (addr & ~PAGEMAP_WALK_MASK) >> |
933 | PAGE_SHIFT; | 970 | PAGE_SHIFT; |
934 | thp_pmd_to_pagemap_entry(&pme, pm, *pmd, offset); | 971 | thp_pmd_to_pagemap_entry(&pme, pm, *pmd, offset, pmd_flags2); |
935 | err = add_to_pagemap(addr, &pme, pm); | 972 | err = add_to_pagemap(addr, &pme, pm); |
936 | if (err) | 973 | if (err) |
937 | break; | 974 | break; |