mm: disable fault around on emulated access bit architecture
[cascardo/linux.git] / mm / memory.c
index 07493e3..a1b93d9 100644 (file)
@@ -1744,6 +1744,7 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
        unsigned long next;
        unsigned long end = addr + PAGE_ALIGN(size);
        struct mm_struct *mm = vma->vm_mm;
+       unsigned long remap_pfn = pfn;
        int err;
 
        /*
@@ -1770,7 +1771,7 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
                vma->vm_pgoff = pfn;
        }
 
-       err = track_pfn_remap(vma, &prot, pfn, addr, PAGE_ALIGN(size));
+       err = track_pfn_remap(vma, &prot, remap_pfn, addr, PAGE_ALIGN(size));
        if (err)
                return -EINVAL;
 
@@ -1789,7 +1790,7 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
        } while (pgd++, addr = next, addr != end);
 
        if (err)
-               untrack_pfn(vma, pfn, PAGE_ALIGN(size));
+               untrack_pfn(vma, remap_pfn, PAGE_ALIGN(size));
 
        return err;
 }
@@ -2875,7 +2876,7 @@ static int __do_fault(struct vm_area_struct *vma, unsigned long address,
  * vm_ops->map_pages.
  */
 void do_set_pte(struct vm_area_struct *vma, unsigned long address,
-               struct page *page, pte_t *pte, bool write, bool anon)
+               struct page *page, pte_t *pte, bool write, bool anon, bool old)
 {
        pte_t entry;
 
@@ -2883,6 +2884,8 @@ void do_set_pte(struct vm_area_struct *vma, unsigned long address,
        entry = mk_pte(page, vma->vm_page_prot);
        if (write)
                entry = maybe_mkwrite(pte_mkdirty(entry), vma);
+       if (old)
+               entry = pte_mkold(entry);
        if (anon) {
                inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
                page_add_new_anon_rmap(page, vma, address, false);
@@ -2896,8 +2899,16 @@ void do_set_pte(struct vm_area_struct *vma, unsigned long address,
        update_mmu_cache(vma, address, pte);
 }
 
+/*
+ * If architecture emulates "accessed" or "young" bit without HW support,
+ * there is no much gain with fault_around.
+ */
 static unsigned long fault_around_bytes __read_mostly =
+#ifndef __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
+       PAGE_SIZE;
+#else
        rounddown_pow_of_two(65536);
+#endif
 
 #ifdef CONFIG_DEBUG_FS
 static int fault_around_bytes_get(void *data, u64 *val)
@@ -3020,9 +3031,20 @@ static int do_read_fault(struct mm_struct *mm, struct vm_area_struct *vma,
         */
        if (vma->vm_ops->map_pages && fault_around_bytes >> PAGE_SHIFT > 1) {
                pte = pte_offset_map_lock(mm, pmd, address, &ptl);
-               do_fault_around(vma, address, pte, pgoff, flags);
                if (!pte_same(*pte, orig_pte))
                        goto unlock_out;
+               do_fault_around(vma, address, pte, pgoff, flags);
+               /* Check if the fault is handled by faultaround */
+               if (!pte_same(*pte, orig_pte)) {
+                       /*
+                        * Faultaround produce old pte, but the pte we've
+                        * handler fault for should be young.
+                        */
+                       pte_t entry = pte_mkyoung(*pte);
+                       if (ptep_set_access_flags(vma, address, pte, entry, 0))
+                               update_mmu_cache(vma, address, pte);
+                       goto unlock_out;
+               }
                pte_unmap_unlock(pte, ptl);
        }
 
@@ -3037,7 +3059,7 @@ static int do_read_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                put_page(fault_page);
                return ret;
        }
-       do_set_pte(vma, address, fault_page, pte, false, false);
+       do_set_pte(vma, address, fault_page, pte, false, false, false);
        unlock_page(fault_page);
 unlock_out:
        pte_unmap_unlock(pte, ptl);
@@ -3089,7 +3111,7 @@ static int do_cow_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                }
                goto uncharge_out;
        }
-       do_set_pte(vma, address, new_page, pte, true, true);
+       do_set_pte(vma, address, new_page, pte, true, true, false);
        mem_cgroup_commit_charge(new_page, memcg, false, false);
        lru_cache_add_active_or_unevictable(new_page, vma);
        pte_unmap_unlock(pte, ptl);
@@ -3146,7 +3168,7 @@ static int do_shared_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                put_page(fault_page);
                return ret;
        }
-       do_set_pte(vma, address, fault_page, pte, true, false);
+       do_set_pte(vma, address, fault_page, pte, true, false, false);
        pte_unmap_unlock(pte, ptl);
 
        if (set_page_dirty(fault_page))