mm/ksm: use folio in write_protect_page
[linux-block.git] / mm / memory.c
index 904f70b994985a7682b59a917522f284fd786950..d592128d7f7d5e879eb460efaa5ca75bab4b6727 100644 (file)
@@ -989,7 +989,7 @@ copy_present_ptes(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma
                        flags |= FPB_IGNORE_SOFT_DIRTY;
 
                nr = folio_pte_batch(folio, addr, src_pte, pte, max_nr, flags,
-                                    &any_writable);
+                                    &any_writable, NULL);
                folio_ref_add(folio, nr);
                if (folio_test_anon(folio)) {
                        if (unlikely(folio_try_dup_anon_rmap_ptes(folio, page,
@@ -1502,10 +1502,15 @@ static __always_inline void zap_present_folio_ptes(struct mmu_gather *tlb,
        if (!delay_rmap) {
                folio_remove_rmap_ptes(folio, page, nr, vma);
 
-               /* Only sanity-check the first page in a batch. */
-               if (unlikely(page_mapcount(page) < 0))
+               if (unlikely(folio_mapcount(folio) < 0))
                        print_bad_pte(vma, addr, ptent, page);
        }
+
+       if (want_init_mlocked_on_free() && folio_test_mlocked(folio) &&
+           !delay_rmap && folio_test_anon(folio)) {
+               kernel_init_pages(page, folio_nr_pages(folio));
+       }
+
        if (unlikely(__tlb_remove_folio_pages(tlb, page, nr, delay_rmap))) {
                *force_flush = true;
                *force_break = true;
@@ -1553,7 +1558,7 @@ static inline int zap_present_ptes(struct mmu_gather *tlb,
         */
        if (unlikely(folio_test_large(folio) && max_nr != 1)) {
                nr = folio_pte_batch(folio, addr, pte, ptent, max_nr, fpb_flags,
-                                    NULL);
+                                    NULL, NULL);
 
                zap_present_folio_ptes(tlb, vma, folio, page, pte, ptent, nr,
                                       addr, details, rss, force_flush,
@@ -1631,12 +1636,13 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
                                folio_remove_rmap_pte(folio, page, vma);
                        folio_put(folio);
                } else if (!non_swap_entry(entry)) {
-                       /* Genuine swap entry, hence a private anon page */
+                       max_nr = (end - addr) / PAGE_SIZE;
+                       nr = swap_pte_batch(pte, max_nr, ptent);
+                       /* Genuine swap entries, hence a private anon pages */
                        if (!should_zap_cows(details))
                                continue;
-                       rss[MM_SWAPENTS]--;
-                       if (unlikely(!free_swap_and_cache(entry)))
-                               print_bad_pte(vma, addr, ptent, NULL);
+                       rss[MM_SWAPENTS] -= nr;
+                       free_swap_and_cache_nr(entry, nr);
                } else if (is_migration_entry(entry)) {
                        folio = pfn_swap_entry_folio(entry);
                        if (!should_zap_folio(details, folio))
@@ -1659,8 +1665,8 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
                        pr_alert("unrecognized swap entry 0x%lx\n", entry.val);
                        WARN_ON_ONCE(1);
                }
-               pte_clear_not_present_full(mm, addr, pte, tlb->fullmm);
-               zap_install_uffd_wp_if_needed(vma, addr, pte, 1, details, ptent);
+               clear_not_present_full_ptes(mm, addr, pte, nr, tlb->fullmm);
+               zap_install_uffd_wp_if_needed(vma, addr, pte, nr, details, ptent);
        } while (pte += nr, addr += PAGE_SIZE * nr, addr != end);
 
        add_mm_rss_vec(mm, rss);
@@ -2765,7 +2771,7 @@ static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud,
        unsigned long next;
        int err = 0;
 
-       BUG_ON(pud_huge(*pud));
+       BUG_ON(pud_leaf(*pud));
 
        if (create) {
                pmd = pmd_alloc_track(mm, pud, addr, mask);
@@ -4190,7 +4196,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
         * when reading from swap. This metadata may be indexed by swap entry
         * so this must be called before swap_free().
         */
-       arch_swap_restore(entry, folio);
+       arch_swap_restore(folio_swap(entry, folio), folio);
 
        /*
         * Remove the swap entry and conditionally try to free up the swapcache.
@@ -4352,6 +4358,9 @@ static struct folio *alloc_anon_folio(struct vm_fault *vmf)
 
        pte_unmap(pte);
 
+       if (!orders)
+               goto fallback;
+
        /* Try allocating the highest of the remaining orders. */
        gfp = vma_thp_gfp_mask(vma);
        while (orders) {
@@ -5035,9 +5044,11 @@ static vm_fault_t do_fault(struct vm_fault *vmf)
        return ret;
 }
 
-int numa_migrate_prep(struct folio *folio, struct vm_area_struct *vma,
+int numa_migrate_prep(struct folio *folio, struct vm_fault *vmf,
                      unsigned long addr, int page_nid, int *flags)
 {
+       struct vm_area_struct *vma = vmf->vma;
+
        folio_get(folio);
 
        /* Record the current PID acceesing VMA */
@@ -5049,7 +5060,55 @@ int numa_migrate_prep(struct folio *folio, struct vm_area_struct *vma,
                *flags |= TNF_FAULT_LOCAL;
        }
 
-       return mpol_misplaced(folio, vma, addr);
+       return mpol_misplaced(folio, vmf, addr);
+}
+
+static void numa_rebuild_single_mapping(struct vm_fault *vmf, struct vm_area_struct *vma,
+                                       unsigned long fault_addr, pte_t *fault_pte,
+                                       bool writable)
+{
+       pte_t pte, old_pte;
+
+       old_pte = ptep_modify_prot_start(vma, fault_addr, fault_pte);
+       pte = pte_modify(old_pte, vma->vm_page_prot);
+       pte = pte_mkyoung(pte);
+       if (writable)
+               pte = pte_mkwrite(pte, vma);
+       ptep_modify_prot_commit(vma, fault_addr, fault_pte, old_pte, pte);
+       update_mmu_cache_range(vmf, vma, fault_addr, fault_pte, 1);
+}
+
+static void numa_rebuild_large_mapping(struct vm_fault *vmf, struct vm_area_struct *vma,
+                                      struct folio *folio, pte_t fault_pte,
+                                      bool ignore_writable, bool pte_write_upgrade)
+{
+       int nr = pte_pfn(fault_pte) - folio_pfn(folio);
+       unsigned long start = max(vmf->address - nr * PAGE_SIZE, vma->vm_start);
+       unsigned long end = min(vmf->address + (folio_nr_pages(folio) - nr) * PAGE_SIZE, vma->vm_end);
+       pte_t *start_ptep = vmf->pte - (vmf->address - start) / PAGE_SIZE;
+       unsigned long addr;
+
+       /* Restore all PTEs' mapping of the large folio */
+       for (addr = start; addr != end; start_ptep++, addr += PAGE_SIZE) {
+               pte_t ptent = ptep_get(start_ptep);
+               bool writable = false;
+
+               if (!pte_present(ptent) || !pte_protnone(ptent))
+                       continue;
+
+               if (pfn_folio(pte_pfn(ptent)) != folio)
+                       continue;
+
+               if (!ignore_writable) {
+                       ptent = pte_modify(ptent, vma->vm_page_prot);
+                       writable = pte_write(ptent);
+                       if (!writable && pte_write_upgrade &&
+                           can_change_pte_writable(vma, addr, ptent))
+                               writable = true;
+               }
+
+               numa_rebuild_single_mapping(vmf, vma, addr, start_ptep, writable);
+       }
 }
 
 static vm_fault_t do_numa_page(struct vm_fault *vmf)
@@ -5057,11 +5116,12 @@ static vm_fault_t do_numa_page(struct vm_fault *vmf)
        struct vm_area_struct *vma = vmf->vma;
        struct folio *folio = NULL;
        int nid = NUMA_NO_NODE;
-       bool writable = false;
+       bool writable = false, ignore_writable = false;
+       bool pte_write_upgrade = vma_wants_manual_pte_write_upgrade(vma);
        int last_cpupid;
        int target_nid;
        pte_t pte, old_pte;
-       int flags = 0;
+       int flags = 0, nr_pages;
 
        /*
         * The pte cannot be used safely until we verify, while holding the page
@@ -5083,7 +5143,7 @@ static vm_fault_t do_numa_page(struct vm_fault *vmf)
         * is only valid while holding the PT lock.
         */
        writable = pte_write(pte);
-       if (!writable && vma_wants_manual_pte_write_upgrade(vma) &&
+       if (!writable && pte_write_upgrade &&
            can_change_pte_writable(vma, vmf->address, pte))
                writable = true;
 
@@ -5091,10 +5151,6 @@ static vm_fault_t do_numa_page(struct vm_fault *vmf)
        if (!folio || folio_is_zone_device(folio))
                goto out_map;
 
-       /* TODO: handle PTE-mapped THP */
-       if (folio_test_large(folio))
-               goto out_map;
-
        /*
         * Avoid grouping on RO pages in general. RO pages shouldn't hurt as
         * much anyway since they can be in shared cache state. This misses
@@ -5110,10 +5166,11 @@ static vm_fault_t do_numa_page(struct vm_fault *vmf)
         * Flag if the folio is shared between multiple address spaces. This
         * is later used when determining whether to group tasks together
         */
-       if (folio_estimated_sharers(folio) > 1 && (vma->vm_flags & VM_SHARED))
+       if (folio_likely_mapped_shared(folio) && (vma->vm_flags & VM_SHARED))
                flags |= TNF_SHARED;
 
        nid = folio_nid(folio);
+       nr_pages = folio_nr_pages(folio);
        /*
         * For memory tiering mode, cpupid of slow memory page is used
         * to record page access time.  So use default value.
@@ -5123,13 +5180,14 @@ static vm_fault_t do_numa_page(struct vm_fault *vmf)
                last_cpupid = (-1 & LAST_CPUPID_MASK);
        else
                last_cpupid = folio_last_cpupid(folio);
-       target_nid = numa_migrate_prep(folio, vma, vmf->address, nid, &flags);
+       target_nid = numa_migrate_prep(folio, vmf, vmf->address, nid, &flags);
        if (target_nid == NUMA_NO_NODE) {
                folio_put(folio);
                goto out_map;
        }
        pte_unmap_unlock(vmf->pte, vmf->ptl);
        writable = false;
+       ignore_writable = true;
 
        /* Migrate to the requested node */
        if (migrate_misplaced_folio(folio, vma, target_nid)) {
@@ -5150,20 +5208,19 @@ static vm_fault_t do_numa_page(struct vm_fault *vmf)
 
 out:
        if (nid != NUMA_NO_NODE)
-               task_numa_fault(last_cpupid, nid, 1, flags);
+               task_numa_fault(last_cpupid, nid, nr_pages, flags);
        return 0;
 out_map:
        /*
         * Make it present again, depending on how arch implements
         * non-accessible ptes, some can allow access by kernel mode.
         */
-       old_pte = ptep_modify_prot_start(vma, vmf->address, vmf->pte);
-       pte = pte_modify(old_pte, vma->vm_page_prot);
-       pte = pte_mkyoung(pte);
-       if (writable)
-               pte = pte_mkwrite(pte, vma);
-       ptep_modify_prot_commit(vma, vmf->address, vmf->pte, old_pte, pte);
-       update_mmu_cache_range(vmf, vma, vmf->address, vmf->pte, 1);
+       if (folio && folio_test_large(folio))
+               numa_rebuild_large_mapping(vmf, vma, folio, pte, ignore_writable,
+                                          pte_write_upgrade);
+       else
+               numa_rebuild_single_mapping(vmf, vma, vmf->address, vmf->pte,
+                                           writable);
        pte_unmap_unlock(vmf->pte, vmf->ptl);
        goto out;
 }
@@ -5868,34 +5925,48 @@ int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
 
 /**
  * follow_pte - look up PTE at a user virtual address
- * @mm: the mm_struct of the target address space
+ * @vma: the memory mapping
  * @address: user virtual address
  * @ptepp: location to store found PTE
  * @ptlp: location to store the lock for the PTE
  *
  * On a successful return, the pointer to the PTE is stored in @ptepp;
  * the corresponding lock is taken and its location is stored in @ptlp.
- * The contents of the PTE are only stable until @ptlp is released;
- * any further use, if any, must be protected against invalidation
- * with MMU notifiers.
+ *
+ * The contents of the PTE are only stable until @ptlp is released using
+ * pte_unmap_unlock(). This function will fail if the PTE is non-present.
+ * Present PTEs may include PTEs that map refcounted pages, such as
+ * anonymous folios in COW mappings.
+ *
+ * Callers must be careful when relying on PTE content after
+ * pte_unmap_unlock(). Especially if the PTE maps a refcounted page,
+ * callers must protect against invalidation with MMU notifiers; otherwise
+ * access to the PFN at a later point in time can trigger use-after-free.
  *
  * Only IO mappings and raw PFN mappings are allowed.  The mmap semaphore
  * should be taken for read.
  *
- * KVM uses this function.  While it is arguably less bad than ``follow_pfn``,
- * it is not a good general-purpose API.
+ * This function must not be used to modify PTE content.
  *
  * Return: zero on success, -ve otherwise.
  */
-int follow_pte(struct mm_struct *mm, unsigned long address,
+int follow_pte(struct vm_area_struct *vma, unsigned long address,
               pte_t **ptepp, spinlock_t **ptlp)
 {
+       struct mm_struct *mm = vma->vm_mm;
        pgd_t *pgd;
        p4d_t *p4d;
        pud_t *pud;
        pmd_t *pmd;
        pte_t *ptep;
 
+       mmap_assert_locked(mm);
+       if (unlikely(address < vma->vm_start || address >= vma->vm_end))
+               goto out;
+
+       if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
+               goto out;
+
        pgd = pgd_offset(mm, address);
        if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
                goto out;
@@ -5925,67 +5996,7 @@ out:
 }
 EXPORT_SYMBOL_GPL(follow_pte);
 
-/**
- * follow_pfn - look up PFN at a user virtual address
- * @vma: memory mapping
- * @address: user virtual address
- * @pfn: location to store found PFN
- *
- * Only IO mappings and raw PFN mappings are allowed.
- *
- * This function does not allow the caller to read the permissions
- * of the PTE.  Do not use it.
- *
- * Return: zero and the pfn at @pfn on success, -ve otherwise.
- */
-int follow_pfn(struct vm_area_struct *vma, unsigned long address,
-       unsigned long *pfn)
-{
-       int ret = -EINVAL;
-       spinlock_t *ptl;
-       pte_t *ptep;
-
-       if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
-               return ret;
-
-       ret = follow_pte(vma->vm_mm, address, &ptep, &ptl);
-       if (ret)
-               return ret;
-       *pfn = pte_pfn(ptep_get(ptep));
-       pte_unmap_unlock(ptep, ptl);
-       return 0;
-}
-EXPORT_SYMBOL(follow_pfn);
-
 #ifdef CONFIG_HAVE_IOREMAP_PROT
-int follow_phys(struct vm_area_struct *vma,
-               unsigned long address, unsigned int flags,
-               unsigned long *prot, resource_size_t *phys)
-{
-       int ret = -EINVAL;
-       pte_t *ptep, pte;
-       spinlock_t *ptl;
-
-       if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
-               goto out;
-
-       if (follow_pte(vma->vm_mm, address, &ptep, &ptl))
-               goto out;
-       pte = ptep_get(ptep);
-
-       if ((flags & FOLL_WRITE) && !pte_write(pte))
-               goto unlock;
-
-       *prot = pgprot_val(pte_pgprot(pte));
-       *phys = (resource_size_t)pte_pfn(pte) << PAGE_SHIFT;
-
-       ret = 0;
-unlock:
-       pte_unmap_unlock(ptep, ptl);
-out:
-       return ret;
-}
-
 /**
  * generic_access_phys - generic implementation for iomem mmap access
  * @vma: the vma to access
@@ -6009,11 +6020,8 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
        int offset = offset_in_page(addr);
        int ret = -EINVAL;
 
-       if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
-               return -EINVAL;
-
 retry:
-       if (follow_pte(vma->vm_mm, addr, &ptep, &ptl))
+       if (follow_pte(vma, addr, &ptep, &ptl))
                return -EINVAL;
        pte = ptep_get(ptep);
        pte_unmap_unlock(ptep, ptl);
@@ -6028,7 +6036,7 @@ retry:
        if (!maddr)
                return -ENOMEM;
 
-       if (follow_pte(vma->vm_mm, addr, &ptep, &ptl))
+       if (follow_pte(vma, addr, &ptep, &ptl))
                goto out_unmap;
 
        if (!pte_same(pte, ptep_get(ptep))) {
@@ -6436,3 +6444,15 @@ void ptlock_free(struct ptdesc *ptdesc)
        kmem_cache_free(page_ptl_cachep, ptdesc->ptl);
 }
 #endif
+
+void vma_pgtable_walk_begin(struct vm_area_struct *vma)
+{
+       if (is_vm_hugetlb_page(vma))
+               hugetlb_vma_lock_read(vma);
+}
+
+void vma_pgtable_walk_end(struct vm_area_struct *vma)
+{
+       if (is_vm_hugetlb_page(vma))
+               hugetlb_vma_unlock_read(vma);
+}