aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc
diff options
context:
space:
mode:
authorCyrill Gorcunov <gorcunov@gmail.com>2013-09-11 17:22:24 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-11 18:57:56 -0400
commitd9104d1ca9662498339c0de975b4666c30485f4e (patch)
treecb95c72dde19930ca985b9834d604958ef4eecde /fs/proc
parent3b11f0aaae830f0f569cb8fb7fd26f4133ebdabd (diff)
mm: track vma changes with VM_SOFTDIRTY bit
Pavel reported that in case if vma area get unmapped and then mapped (or expanded) in-place, the soft dirty tracker won't be able to recognize this situation since it works on pte level and ptes are get zapped on unmap, loosing soft dirty bit of course. So to resolve this situation we need to track actions on vma level, there VM_SOFTDIRTY flag comes in. When new vma area created (or old expanded) we set this bit, and keep it here until application calls for clearing soft dirty bit. Thus when user space application track memory changes now it can detect if vma area is renewed. Reported-by: Pavel Emelyanov <xemul@parallels.com> Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Matt Mackall <mpm@selenic.com> Cc: Xiao Guangrong <xiaoguangrong@linux.vnet.ibm.com> Cc: Marcelo Tosatti <mtosatti@redhat.com> Cc: KOSAKI Motohiro <kosaki.motohiro@gmail.com> Cc: Stephen Rothwell <sfr@canb.auug.org.au> Cc: Peter Zijlstra <peterz@infradead.org> Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com> Cc: Rob Landley <rob@landley.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc')
-rw-r--r--fs/proc/task_mmu.c46
1 files changed, 36 insertions, 10 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 107d026f5d6e..09228639b83d 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -740,6 +740,9 @@ static inline void clear_soft_dirty(struct vm_area_struct *vma,
740 ptent = pte_file_clear_soft_dirty(ptent); 740 ptent = pte_file_clear_soft_dirty(ptent);
741 } 741 }
742 742
743 if (vma->vm_flags & VM_SOFTDIRTY)
744 vma->vm_flags &= ~VM_SOFTDIRTY;
745
743 set_pte_at(vma->vm_mm, addr, pte, ptent); 746 set_pte_at(vma->vm_mm, addr, pte, ptent);
744#endif 747#endif
745} 748}
@@ -949,13 +952,15 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm,
949 if (is_migration_entry(entry)) 952 if (is_migration_entry(entry))
950 page = migration_entry_to_page(entry); 953 page = migration_entry_to_page(entry);
951 } else { 954 } else {
952 *pme = make_pme(PM_NOT_PRESENT(pm->v2)); 955 if (vma->vm_flags & VM_SOFTDIRTY)
956 flags2 |= __PM_SOFT_DIRTY;
957 *pme = make_pme(PM_NOT_PRESENT(pm->v2) | PM_STATUS2(pm->v2, flags2));
953 return; 958 return;
954 } 959 }
955 960
956 if (page && !PageAnon(page)) 961 if (page && !PageAnon(page))
957 flags |= PM_FILE; 962 flags |= PM_FILE;
958 if (pte_soft_dirty(pte)) 963 if ((vma->vm_flags & VM_SOFTDIRTY) || pte_soft_dirty(pte))
959 flags2 |= __PM_SOFT_DIRTY; 964 flags2 |= __PM_SOFT_DIRTY;
960 965
961 *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, flags2) | flags); 966 *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, flags2) | flags);
@@ -974,7 +979,7 @@ static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *p
974 *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) 979 *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset)
975 | PM_STATUS2(pm->v2, pmd_flags2) | PM_PRESENT); 980 | PM_STATUS2(pm->v2, pmd_flags2) | PM_PRESENT);
976 else 981 else
977 *pme = make_pme(PM_NOT_PRESENT(pm->v2)); 982 *pme = make_pme(PM_NOT_PRESENT(pm->v2) | PM_STATUS2(pm->v2, pmd_flags2));
978} 983}
979#else 984#else
980static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, 985static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm,
@@ -997,7 +1002,11 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
997 if (vma && pmd_trans_huge_lock(pmd, vma) == 1) { 1002 if (vma && pmd_trans_huge_lock(pmd, vma) == 1) {
998 int pmd_flags2; 1003 int pmd_flags2;
999 1004
1000 pmd_flags2 = (pmd_soft_dirty(*pmd) ? __PM_SOFT_DIRTY : 0); 1005 if ((vma->vm_flags & VM_SOFTDIRTY) || pmd_soft_dirty(*pmd))
1006 pmd_flags2 = __PM_SOFT_DIRTY;
1007 else
1008 pmd_flags2 = 0;
1009
1001 for (; addr != end; addr += PAGE_SIZE) { 1010 for (; addr != end; addr += PAGE_SIZE) {
1002 unsigned long offset; 1011 unsigned long offset;
1003 1012
@@ -1015,12 +1024,17 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
1015 if (pmd_trans_unstable(pmd)) 1024 if (pmd_trans_unstable(pmd))
1016 return 0; 1025 return 0;
1017 for (; addr != end; addr += PAGE_SIZE) { 1026 for (; addr != end; addr += PAGE_SIZE) {
1027 int flags2;
1018 1028
1019 /* check to see if we've left 'vma' behind 1029 /* check to see if we've left 'vma' behind
1020 * and need a new, higher one */ 1030 * and need a new, higher one */
1021 if (vma && (addr >= vma->vm_end)) { 1031 if (vma && (addr >= vma->vm_end)) {
1022 vma = find_vma(walk->mm, addr); 1032 vma = find_vma(walk->mm, addr);
1023 pme = make_pme(PM_NOT_PRESENT(pm->v2)); 1033 if (vma && (vma->vm_flags & VM_SOFTDIRTY))
1034 flags2 = __PM_SOFT_DIRTY;
1035 else
1036 flags2 = 0;
1037 pme = make_pme(PM_NOT_PRESENT(pm->v2) | PM_STATUS2(pm->v2, flags2));
1024 } 1038 }
1025 1039
1026 /* check that 'vma' actually covers this address, 1040 /* check that 'vma' actually covers this address,
@@ -1044,13 +1058,15 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
1044 1058
1045#ifdef CONFIG_HUGETLB_PAGE 1059#ifdef CONFIG_HUGETLB_PAGE
1046static void huge_pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, 1060static void huge_pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm,
1047 pte_t pte, int offset) 1061 pte_t pte, int offset, int flags2)
1048{ 1062{
1049 if (pte_present(pte)) 1063 if (pte_present(pte))
1050 *pme = make_pme(PM_PFRAME(pte_pfn(pte) + offset) 1064 *pme = make_pme(PM_PFRAME(pte_pfn(pte) + offset) |
1051 | PM_STATUS2(pm->v2, 0) | PM_PRESENT); 1065 PM_STATUS2(pm->v2, flags2) |
1066 PM_PRESENT);
1052 else 1067 else
1053 *pme = make_pme(PM_NOT_PRESENT(pm->v2)); 1068 *pme = make_pme(PM_NOT_PRESENT(pm->v2) |
1069 PM_STATUS2(pm->v2, flags2));
1054} 1070}
1055 1071
1056/* This function walks within one hugetlb entry in the single call */ 1072/* This function walks within one hugetlb entry in the single call */
@@ -1059,12 +1075,22 @@ static int pagemap_hugetlb_range(pte_t *pte, unsigned long hmask,
1059 struct mm_walk *walk) 1075 struct mm_walk *walk)
1060{ 1076{
1061 struct pagemapread *pm = walk->private; 1077 struct pagemapread *pm = walk->private;
1078 struct vm_area_struct *vma;
1062 int err = 0; 1079 int err = 0;
1080 int flags2;
1063 pagemap_entry_t pme; 1081 pagemap_entry_t pme;
1064 1082
1083 vma = find_vma(walk->mm, addr);
1084 WARN_ON_ONCE(!vma);
1085
1086 if (vma && (vma->vm_flags & VM_SOFTDIRTY))
1087 flags2 = __PM_SOFT_DIRTY;
1088 else
1089 flags2 = 0;
1090
1065 for (; addr != end; addr += PAGE_SIZE) { 1091 for (; addr != end; addr += PAGE_SIZE) {
1066 int offset = (addr & ~hmask) >> PAGE_SHIFT; 1092 int offset = (addr & ~hmask) >> PAGE_SHIFT;
1067 huge_pte_to_pagemap_entry(&pme, pm, *pte, offset); 1093 huge_pte_to_pagemap_entry(&pme, pm, *pte, offset, flags2);
1068 err = add_to_pagemap(addr, &pme, pm); 1094 err = add_to_pagemap(addr, &pme, pm);
1069 if (err) 1095 if (err)
1070 return err; 1096 return err;