]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
mm: thp: set the accessed flag for old pages on access fault
authorWill Deacon <will.deacon@arm.com>
Sat, 3 Nov 2012 00:42:23 +0000 (11:42 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Wed, 7 Nov 2012 04:15:12 +0000 (15:15 +1100)
On x86 memory accesses to pages without the ACCESSED flag set result in
the ACCESSED flag being set automatically.  With the ARM architecture a
page access fault is raised instead (and it will continue to be raised
until the ACCESSED flag is set for the appropriate PTE/PMD).

For normal memory pages, handle_pte_fault will call pte_mkyoung
(effectively setting the ACCESSED flag).  For transparent huge pages,
pmd_mkyoung will only be called for a write fault.

This patch ensures that faults on transparent hugepages which do not
result in a CoW update the access flags for the faulting pmd.

Signed-off-by: Will Deacon <will.deacon@arm.com>
Cc: Chris Metcalf <cmetcalf@tilera.com>
Acked-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Ni zhan Chen <nizhan.chen@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/huge_mm.h
mm/huge_memory.c
mm/memory.c

index 4f0f948601388802d01b9c4b176df8431ace61e6..766fb27bf13f53d19568dea72a212693879de8f9 100644 (file)
@@ -8,6 +8,10 @@ extern int do_huge_pmd_anonymous_page(struct mm_struct *mm,
 extern int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                         pmd_t *dst_pmd, pmd_t *src_pmd, unsigned long addr,
                         struct vm_area_struct *vma);
+extern void huge_pmd_set_accessed(struct mm_struct *mm,
+                                 struct vm_area_struct *vma,
+                                 unsigned long address, pmd_t *pmd,
+                                 pmd_t orig_pmd, int dirty);
 extern int do_huge_pmd_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
                               unsigned long address, pmd_t *pmd,
                               pmd_t orig_pmd);
index f1c26797119838d8bc26814da26e3318e2a32897..fc77f1b5118cf334492f9d6a7cea14b1c90b9127 100644 (file)
@@ -932,6 +932,28 @@ out:
        return ret;
 }
 
+void huge_pmd_set_accessed(struct mm_struct *mm,
+                          struct vm_area_struct *vma,
+                          unsigned long address,
+                          pmd_t *pmd, pmd_t orig_pmd,
+                          int dirty)
+{
+       pmd_t entry;
+       unsigned long haddr;
+
+       spin_lock(&mm->page_table_lock);
+       if (unlikely(!pmd_same(*pmd, orig_pmd)))
+               goto unlock;
+
+       entry = pmd_mkyoung(orig_pmd);
+       haddr = address & HPAGE_PMD_MASK;
+       if (pmdp_set_access_flags(vma, haddr, pmd, entry, dirty))
+               update_mmu_cache_pmd(vma, address, pmd);
+
+unlock:
+       spin_unlock(&mm->page_table_lock);
+}
+
 static int do_huge_pmd_wp_page_fallback(struct mm_struct *mm,
                                        struct vm_area_struct *vma,
                                        unsigned long address,
index b197b19d540073017d0cb68614066789ac81b1fe..59754bbd0dcc920a262058177b9978c12553783e 100644 (file)
@@ -3648,12 +3648,14 @@ retry:
 
                barrier();
                if (pmd_trans_huge(orig_pmd) && !pmd_trans_splitting(orig_pmd)) {
+                       unsigned int dirty = flags & FAULT_FLAG_WRITE;
+
                        if (pmd_numa(vma, orig_pmd)) {
                                do_huge_pmd_numa_page(mm, vma, address, pmd,
                                                      flags, orig_pmd);
                        }
 
-                       if ((flags & FAULT_FLAG_WRITE) && !pmd_write(orig_pmd)) {
+                       if (dirty && !pmd_write(orig_pmd)) {
                                ret = do_huge_pmd_wp_page(mm, vma, address, pmd,
                                                          orig_pmd);
                                /*
@@ -3663,6 +3665,9 @@ retry:
                                 */
                                if (unlikely(ret & VM_FAULT_OOM))
                                        goto retry;
+                       } else {
+                               huge_pmd_set_accessed(mm, vma, address, pmd,
+                                                     orig_pmd, dirty);
                        }
 
                        return ret;