Commit 87e6d49a000fa6148bd539f632706b02c3485c5d

Authored by Peter Feiner
Committed by Linus Torvalds
1 parent 5760a97c71

mm: softdirty: addresses before VMAs in PTE holes aren't softdirty

In PTE holes that contain VM_SOFTDIRTY VMAs, unmapped addresses before
VM_SOFTDIRTY VMAs are reported as softdirty by /proc/pid/pagemap.  This
bug was introduced in commit 68b5a6524856 ("mm: softdirty: respect
VM_SOFTDIRTY in PTE holes").  That commit made /proc/pid/pagemap look at
VM_SOFTDIRTY in PTE holes but neglected to observe the start of VMAs
returned by find_vma.

Tested:
  Wrote a selftest that creates a PMD-sized VMA then unmaps the first
  page and asserts that the page is not softdirty. I'm going to send the
  pagemap selftest in a later commit.

Signed-off-by: Peter Feiner <pfeiner@google.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Pavel Emelyanov <xemul@parallels.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Cc: Jamie Liu <jamieliu@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 1 changed file with 18 additions and 9 deletions Side-by-side Diff

... ... @@ -931,23 +931,32 @@
931 931 while (addr < end) {
932 932 struct vm_area_struct *vma = find_vma(walk->mm, addr);
933 933 pagemap_entry_t pme = make_pme(PM_NOT_PRESENT(pm->v2));
934   - unsigned long vm_end;
  934 + /* End of address space hole, which we mark as non-present. */
  935 + unsigned long hole_end;
935 936  
936   - if (!vma) {
937   - vm_end = end;
938   - } else {
939   - vm_end = min(end, vma->vm_end);
940   - if (vma->vm_flags & VM_SOFTDIRTY)
941   - pme.pme |= PM_STATUS2(pm->v2, __PM_SOFT_DIRTY);
  937 + if (vma)
  938 + hole_end = min(end, vma->vm_start);
  939 + else
  940 + hole_end = end;
  941 +
  942 + for (; addr < hole_end; addr += PAGE_SIZE) {
  943 + err = add_to_pagemap(addr, &pme, pm);
  944 + if (err)
  945 + goto out;
942 946 }
943 947  
944   - for (; addr < vm_end; addr += PAGE_SIZE) {
  948 + if (!vma)
  949 + break;
  950 +
  951 + /* Addresses in the VMA. */
  952 + if (vma->vm_flags & VM_SOFTDIRTY)
  953 + pme.pme |= PM_STATUS2(pm->v2, __PM_SOFT_DIRTY);
  954 + for (; addr < min(end, vma->vm_end); addr += PAGE_SIZE) {
945 955 err = add_to_pagemap(addr, &pme, pm);
946 956 if (err)
947 957 goto out;
948 958 }
949 959 }
950   -
951 960 out:
952 961 return err;
953 962 }