]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - mm/huge_memory.c
mtd: nand: denali: allow to override revision number
[karo-tx-linux.git] / mm / huge_memory.c
index 5f3ad65c85de01fa6e4c8a07ef9494410bf2b133..d36b2af4d1bf4b6621974823f36c52dda405f181 100644 (file)
@@ -9,6 +9,8 @@
 
 #include <linux/mm.h>
 #include <linux/sched.h>
+#include <linux/sched/coredump.h>
+#include <linux/sched/numa_balancing.h>
 #include <linux/highmem.h>
 #include <linux/hugetlb.h>
 #include <linux/mmu_notifier.h>
@@ -142,42 +144,6 @@ static struct shrinker huge_zero_page_shrinker = {
 };
 
 #ifdef CONFIG_SYSFS
-
-static ssize_t triple_flag_store(struct kobject *kobj,
-                                struct kobj_attribute *attr,
-                                const char *buf, size_t count,
-                                enum transparent_hugepage_flag enabled,
-                                enum transparent_hugepage_flag deferred,
-                                enum transparent_hugepage_flag req_madv)
-{
-       if (!memcmp("defer", buf,
-                   min(sizeof("defer")-1, count))) {
-               if (enabled == deferred)
-                       return -EINVAL;
-               clear_bit(enabled, &transparent_hugepage_flags);
-               clear_bit(req_madv, &transparent_hugepage_flags);
-               set_bit(deferred, &transparent_hugepage_flags);
-       } else if (!memcmp("always", buf,
-                   min(sizeof("always")-1, count))) {
-               clear_bit(deferred, &transparent_hugepage_flags);
-               clear_bit(req_madv, &transparent_hugepage_flags);
-               set_bit(enabled, &transparent_hugepage_flags);
-       } else if (!memcmp("madvise", buf,
-                          min(sizeof("madvise")-1, count))) {
-               clear_bit(enabled, &transparent_hugepage_flags);
-               clear_bit(deferred, &transparent_hugepage_flags);
-               set_bit(req_madv, &transparent_hugepage_flags);
-       } else if (!memcmp("never", buf,
-                          min(sizeof("never")-1, count))) {
-               clear_bit(enabled, &transparent_hugepage_flags);
-               clear_bit(req_madv, &transparent_hugepage_flags);
-               clear_bit(deferred, &transparent_hugepage_flags);
-       } else
-               return -EINVAL;
-
-       return count;
-}
-
 static ssize_t enabled_show(struct kobject *kobj,
                            struct kobj_attribute *attr, char *buf)
 {
@@ -193,19 +159,28 @@ static ssize_t enabled_store(struct kobject *kobj,
                             struct kobj_attribute *attr,
                             const char *buf, size_t count)
 {
-       ssize_t ret;
+       ssize_t ret = count;
 
-       ret = triple_flag_store(kobj, attr, buf, count,
-                               TRANSPARENT_HUGEPAGE_FLAG,
-                               TRANSPARENT_HUGEPAGE_FLAG,
-                               TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG);
+       if (!memcmp("always", buf,
+                   min(sizeof("always")-1, count))) {
+               clear_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags);
+               set_bit(TRANSPARENT_HUGEPAGE_FLAG, &transparent_hugepage_flags);
+       } else if (!memcmp("madvise", buf,
+                          min(sizeof("madvise")-1, count))) {
+               clear_bit(TRANSPARENT_HUGEPAGE_FLAG, &transparent_hugepage_flags);
+               set_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags);
+       } else if (!memcmp("never", buf,
+                          min(sizeof("never")-1, count))) {
+               clear_bit(TRANSPARENT_HUGEPAGE_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG, &transparent_hugepage_flags);
+       } else
+               ret = -EINVAL;
 
        if (ret > 0) {
                int err = start_stop_khugepaged();
                if (err)
                        ret = err;
        }
-
        return ret;
 }
 static struct kobj_attribute enabled_attr =
@@ -241,32 +216,58 @@ ssize_t single_hugepage_flag_store(struct kobject *kobj,
        return count;
 }
 
-/*
- * Currently defrag only disables __GFP_NOWAIT for allocation. A blind
- * __GFP_REPEAT is too aggressive, it's never worth swapping tons of
- * memory just to allocate one more hugepage.
- */
 static ssize_t defrag_show(struct kobject *kobj,
                           struct kobj_attribute *attr, char *buf)
 {
        if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags))
-               return sprintf(buf, "[always] defer madvise never\n");
+               return sprintf(buf, "[always] defer defer+madvise madvise never\n");
        if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags))
-               return sprintf(buf, "always [defer] madvise never\n");
-       else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags))
-               return sprintf(buf, "always defer [madvise] never\n");
-       else
-               return sprintf(buf, "always defer madvise [never]\n");
-
+               return sprintf(buf, "always [defer] defer+madvise madvise never\n");
+       if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags))
+               return sprintf(buf, "always defer [defer+madvise] madvise never\n");
+       if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags))
+               return sprintf(buf, "always defer defer+madvise [madvise] never\n");
+       return sprintf(buf, "always defer defer+madvise madvise [never]\n");
 }
+
 static ssize_t defrag_store(struct kobject *kobj,
                            struct kobj_attribute *attr,
                            const char *buf, size_t count)
 {
-       return triple_flag_store(kobj, attr, buf, count,
-                                TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG,
-                                TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG,
-                                TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG);
+       if (!memcmp("always", buf,
+                   min(sizeof("always")-1, count))) {
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags);
+               set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags);
+       } else if (!memcmp("defer", buf,
+                   min(sizeof("defer")-1, count))) {
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags);
+               set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags);
+       } else if (!memcmp("defer+madvise", buf,
+                   min(sizeof("defer+madvise")-1, count))) {
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags);
+               set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags);
+       } else if (!memcmp("madvise", buf,
+                          min(sizeof("madvise")-1, count))) {
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags);
+               set_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags);
+       } else if (!memcmp("never", buf,
+                          min(sizeof("never")-1, count))) {
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags);
+               clear_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags);
+       } else
+               return -EINVAL;
+
+       return count;
 }
 static struct kobj_attribute defrag_attr =
        __ATTR(defrag, 0644, defrag_show, defrag_store);
@@ -612,25 +613,28 @@ static int __do_huge_pmd_anonymous_page(struct vm_fault *vmf, struct page *page,
 }
 
 /*
- * If THP defrag is set to always then directly reclaim/compact as necessary
- * If set to defer then do only background reclaim/compact and defer to khugepaged
- * If set to madvise and the VMA is flagged then directly reclaim/compact
- * When direct reclaim/compact is allowed, don't retry except for flagged VMA's
+ * always: directly stall for all thp allocations
+ * defer: wake kswapd and fail if not immediately available
+ * defer+madvise: wake kswapd and directly stall for MADV_HUGEPAGE, otherwise
+ *               fail if not immediately available
+ * madvise: directly stall for MADV_HUGEPAGE, otherwise fail if not immediately
+ *         available
+ * never: never stall for any thp allocation
  */
 static inline gfp_t alloc_hugepage_direct_gfpmask(struct vm_area_struct *vma)
 {
-       bool vma_madvised = !!(vma->vm_flags & VM_HUGEPAGE);
+       const bool vma_madvised = !!(vma->vm_flags & VM_HUGEPAGE);
 
-       if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG,
-                               &transparent_hugepage_flags) && vma_madvised)
-               return GFP_TRANSHUGE;
-       else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG,
-                                               &transparent_hugepage_flags))
-               return GFP_TRANSHUGE_LIGHT | __GFP_KSWAPD_RECLAIM;
-       else if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG,
-                                               &transparent_hugepage_flags))
+       if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG, &transparent_hugepage_flags))
                return GFP_TRANSHUGE | (vma_madvised ? 0 : __GFP_NORETRY);
-
+       if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG, &transparent_hugepage_flags))
+               return GFP_TRANSHUGE_LIGHT | __GFP_KSWAPD_RECLAIM;
+       if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG, &transparent_hugepage_flags))
+               return GFP_TRANSHUGE_LIGHT | (vma_madvised ? __GFP_DIRECT_RECLAIM :
+                                                            __GFP_KSWAPD_RECLAIM);
+       if (test_bit(TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG, &transparent_hugepage_flags))
+               return GFP_TRANSHUGE_LIGHT | (vma_madvised ? __GFP_DIRECT_RECLAIM :
+                                                            0);
        return GFP_TRANSHUGE_LIGHT;
 }
 
@@ -755,6 +759,60 @@ int vmf_insert_pfn_pmd(struct vm_area_struct *vma, unsigned long addr,
 }
 EXPORT_SYMBOL_GPL(vmf_insert_pfn_pmd);
 
+#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
+static pud_t maybe_pud_mkwrite(pud_t pud, struct vm_area_struct *vma)
+{
+       if (likely(vma->vm_flags & VM_WRITE))
+               pud = pud_mkwrite(pud);
+       return pud;
+}
+
+static void insert_pfn_pud(struct vm_area_struct *vma, unsigned long addr,
+               pud_t *pud, pfn_t pfn, pgprot_t prot, bool write)
+{
+       struct mm_struct *mm = vma->vm_mm;
+       pud_t entry;
+       spinlock_t *ptl;
+
+       ptl = pud_lock(mm, pud);
+       entry = pud_mkhuge(pfn_t_pud(pfn, prot));
+       if (pfn_t_devmap(pfn))
+               entry = pud_mkdevmap(entry);
+       if (write) {
+               entry = pud_mkyoung(pud_mkdirty(entry));
+               entry = maybe_pud_mkwrite(entry, vma);
+       }
+       set_pud_at(mm, addr, pud, entry);
+       update_mmu_cache_pud(vma, addr, pud);
+       spin_unlock(ptl);
+}
+
+int vmf_insert_pfn_pud(struct vm_area_struct *vma, unsigned long addr,
+                       pud_t *pud, pfn_t pfn, bool write)
+{
+       pgprot_t pgprot = vma->vm_page_prot;
+       /*
+        * If we had pud_special, we could avoid all these restrictions,
+        * but we need to be consistent with PTEs and architectures that
+        * can't support a 'special' bit.
+        */
+       BUG_ON(!(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)));
+       BUG_ON((vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) ==
+                                               (VM_PFNMAP|VM_MIXEDMAP));
+       BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags));
+       BUG_ON(!pfn_t_devmap(pfn));
+
+       if (addr < vma->vm_start || addr >= vma->vm_end)
+               return VM_FAULT_SIGBUS;
+
+       track_pfn_insert(vma, &pgprot, pfn);
+
+       insert_pfn_pud(vma, addr, pud, pfn, pgprot, write);
+       return VM_FAULT_NOPAGE;
+}
+EXPORT_SYMBOL_GPL(vmf_insert_pfn_pud);
+#endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
+
 static void touch_pmd(struct vm_area_struct *vma, unsigned long addr,
                pmd_t *pmd)
 {
@@ -885,6 +943,123 @@ out:
        return ret;
 }
 
+#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
+static void touch_pud(struct vm_area_struct *vma, unsigned long addr,
+               pud_t *pud)
+{
+       pud_t _pud;
+
+       /*
+        * We should set the dirty bit only for FOLL_WRITE but for now
+        * the dirty bit in the pud is meaningless.  And if the dirty
+        * bit will become meaningful and we'll only set it with
+        * FOLL_WRITE, an atomic set_bit will be required on the pud to
+        * set the young bit, instead of the current set_pud_at.
+        */
+       _pud = pud_mkyoung(pud_mkdirty(*pud));
+       if (pudp_set_access_flags(vma, addr & HPAGE_PUD_MASK,
+                               pud, _pud,  1))
+               update_mmu_cache_pud(vma, addr, pud);
+}
+
+struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
+               pud_t *pud, int flags)
+{
+       unsigned long pfn = pud_pfn(*pud);
+       struct mm_struct *mm = vma->vm_mm;
+       struct dev_pagemap *pgmap;
+       struct page *page;
+
+       assert_spin_locked(pud_lockptr(mm, pud));
+
+       if (flags & FOLL_WRITE && !pud_write(*pud))
+               return NULL;
+
+       if (pud_present(*pud) && pud_devmap(*pud))
+               /* pass */;
+       else
+               return NULL;
+
+       if (flags & FOLL_TOUCH)
+               touch_pud(vma, addr, pud);
+
+       /*
+        * device mapped pages can only be returned if the
+        * caller will manage the page reference count.
+        */
+       if (!(flags & FOLL_GET))
+               return ERR_PTR(-EEXIST);
+
+       pfn += (addr & ~PUD_MASK) >> PAGE_SHIFT;
+       pgmap = get_dev_pagemap(pfn, NULL);
+       if (!pgmap)
+               return ERR_PTR(-EFAULT);
+       page = pfn_to_page(pfn);
+       get_page(page);
+       put_dev_pagemap(pgmap);
+
+       return page;
+}
+
+int copy_huge_pud(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+                 pud_t *dst_pud, pud_t *src_pud, unsigned long addr,
+                 struct vm_area_struct *vma)
+{
+       spinlock_t *dst_ptl, *src_ptl;
+       pud_t pud;
+       int ret;
+
+       dst_ptl = pud_lock(dst_mm, dst_pud);
+       src_ptl = pud_lockptr(src_mm, src_pud);
+       spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING);
+
+       ret = -EAGAIN;
+       pud = *src_pud;
+       if (unlikely(!pud_trans_huge(pud) && !pud_devmap(pud)))
+               goto out_unlock;
+
+       /*
+        * When page table lock is held, the huge zero pud should not be
+        * under splitting since we don't split the page itself, only pud to
+        * a page table.
+        */
+       if (is_huge_zero_pud(pud)) {
+               /* No huge zero pud yet */
+       }
+
+       pudp_set_wrprotect(src_mm, addr, src_pud);
+       pud = pud_mkold(pud_wrprotect(pud));
+       set_pud_at(dst_mm, addr, dst_pud, pud);
+
+       ret = 0;
+out_unlock:
+       spin_unlock(src_ptl);
+       spin_unlock(dst_ptl);
+       return ret;
+}
+
+void huge_pud_set_accessed(struct vm_fault *vmf, pud_t orig_pud)
+{
+       pud_t entry;
+       unsigned long haddr;
+       bool write = vmf->flags & FAULT_FLAG_WRITE;
+
+       vmf->ptl = pud_lock(vmf->vma->vm_mm, vmf->pud);
+       if (unlikely(!pud_same(*vmf->pud, orig_pud)))
+               goto unlock;
+
+       entry = pud_mkyoung(orig_pud);
+       if (write)
+               entry = pud_mkdirty(entry);
+       haddr = vmf->address & HPAGE_PUD_MASK;
+       if (pudp_set_access_flags(vmf->vma, haddr, vmf->pud, entry, write))
+               update_mmu_cache_pud(vmf->vma, vmf->address, vmf->pud);
+
+unlock:
+       spin_unlock(vmf->ptl);
+}
+#endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
+
 void huge_pmd_set_accessed(struct vm_fault *vmf, pmd_t orig_pmd)
 {
        pmd_t entry;
@@ -1253,7 +1428,7 @@ int do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t pmd)
        }
 
        /* See similar comment in do_numa_page for explanation */
-       if (!pmd_write(pmd))
+       if (!pmd_savedwrite(pmd))
                flags |= TNF_NO_GROUP;
 
        /*
@@ -1316,7 +1491,7 @@ int do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t pmd)
        goto out;
 clear_pmdnuma:
        BUG_ON(!PageLocked(page));
-       was_writable = pmd_write(pmd);
+       was_writable = pmd_savedwrite(pmd);
        pmd = pmd_modify(pmd, vma->vm_page_prot);
        pmd = pmd_mkyoung(pmd);
        if (was_writable)
@@ -1333,7 +1508,7 @@ out:
 
        if (page_nid != -1)
                task_numa_fault(last_cpupid, page_nid, HPAGE_PMD_NR,
-                               vmf->flags);
+                               flags);
 
        return 0;
 }
@@ -1571,7 +1746,7 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
                        entry = pmdp_huge_get_and_clear_notify(mm, addr, pmd);
                        entry = pmd_modify(entry, newprot);
                        if (preserve_write)
-                               entry = pmd_mkwrite(entry);
+                               entry = pmd_mk_savedwrite(entry);
                        ret = HPAGE_PMD_NR;
                        set_pmd_at(mm, addr, pmd, entry);
                        BUG_ON(vma_is_anonymous(vma) && !preserve_write &&
@@ -1599,6 +1774,84 @@ spinlock_t *__pmd_trans_huge_lock(pmd_t *pmd, struct vm_area_struct *vma)
        return NULL;
 }
 
+/*
+ * Returns true if a given pud maps a thp, false otherwise.
+ *
+ * Note that if it returns true, this routine returns without unlocking page
+ * table lock. So callers must unlock it.
+ */
+spinlock_t *__pud_trans_huge_lock(pud_t *pud, struct vm_area_struct *vma)
+{
+       spinlock_t *ptl;
+
+       ptl = pud_lock(vma->vm_mm, pud);
+       if (likely(pud_trans_huge(*pud) || pud_devmap(*pud)))
+               return ptl;
+       spin_unlock(ptl);
+       return NULL;
+}
+
+#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
+int zap_huge_pud(struct mmu_gather *tlb, struct vm_area_struct *vma,
+                pud_t *pud, unsigned long addr)
+{
+       pud_t orig_pud;
+       spinlock_t *ptl;
+
+       ptl = __pud_trans_huge_lock(pud, vma);
+       if (!ptl)
+               return 0;
+       /*
+        * For architectures like ppc64 we look at deposited pgtable
+        * when calling pudp_huge_get_and_clear. So do the
+        * pgtable_trans_huge_withdraw after finishing pudp related
+        * operations.
+        */
+       orig_pud = pudp_huge_get_and_clear_full(tlb->mm, addr, pud,
+                       tlb->fullmm);
+       tlb_remove_pud_tlb_entry(tlb, pud, addr);
+       if (vma_is_dax(vma)) {
+               spin_unlock(ptl);
+               /* No zero page support yet */
+       } else {
+               /* No support for anonymous PUD pages yet */
+               BUG();
+       }
+       return 1;
+}
+
+static void __split_huge_pud_locked(struct vm_area_struct *vma, pud_t *pud,
+               unsigned long haddr)
+{
+       VM_BUG_ON(haddr & ~HPAGE_PUD_MASK);
+       VM_BUG_ON_VMA(vma->vm_start > haddr, vma);
+       VM_BUG_ON_VMA(vma->vm_end < haddr + HPAGE_PUD_SIZE, vma);
+       VM_BUG_ON(!pud_trans_huge(*pud) && !pud_devmap(*pud));
+
+       count_vm_event(THP_SPLIT_PMD);
+
+       pudp_huge_clear_flush_notify(vma, haddr, pud);
+}
+
+void __split_huge_pud(struct vm_area_struct *vma, pud_t *pud,
+               unsigned long address)
+{
+       spinlock_t *ptl;
+       struct mm_struct *mm = vma->vm_mm;
+       unsigned long haddr = address & HPAGE_PUD_MASK;
+
+       mmu_notifier_invalidate_range_start(mm, haddr, haddr + HPAGE_PUD_SIZE);
+       ptl = pud_lock(mm, pud);
+       if (unlikely(!pud_trans_huge(*pud) && !pud_devmap(*pud)))
+               goto out;
+       __split_huge_pud_locked(vma, pud, haddr);
+
+out:
+       spin_unlock(ptl);
+       mmu_notifier_invalidate_range_end(mm, haddr, haddr + HPAGE_PUD_SIZE);
+}
+#endif /* CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
+
 static void __split_huge_zero_page_pmd(struct vm_area_struct *vma,
                unsigned long haddr, pmd_t *pmd)
 {
@@ -1855,32 +2108,27 @@ void vma_adjust_trans_huge(struct vm_area_struct *vma,
 static void freeze_page(struct page *page)
 {
        enum ttu_flags ttu_flags = TTU_IGNORE_MLOCK | TTU_IGNORE_ACCESS |
-               TTU_RMAP_LOCKED;
-       int i, ret;
+               TTU_RMAP_LOCKED | TTU_SPLIT_HUGE_PMD;
+       int ret;
 
        VM_BUG_ON_PAGE(!PageHead(page), page);
 
        if (PageAnon(page))
                ttu_flags |= TTU_MIGRATION;
 
-       /* We only need TTU_SPLIT_HUGE_PMD once */
-       ret = try_to_unmap(page, ttu_flags | TTU_SPLIT_HUGE_PMD);
-       for (i = 1; !ret && i < HPAGE_PMD_NR; i++) {
-               /* Cut short if the page is unmapped */
-               if (page_count(page) == 1)
-                       return;
-
-               ret = try_to_unmap(page + i, ttu_flags);
-       }
-       VM_BUG_ON_PAGE(ret, page + i - 1);
+       ret = try_to_unmap(page, ttu_flags);
+       VM_BUG_ON_PAGE(ret, page);
 }
 
 static void unfreeze_page(struct page *page)
 {
        int i;
-
-       for (i = 0; i < HPAGE_PMD_NR; i++)
-               remove_migration_ptes(page + i, page + i, true);
+       if (PageTransHuge(page)) {
+               remove_migration_ptes(page, page, true);
+       } else {
+               for (i = 0; i < HPAGE_PMD_NR; i++)
+                       remove_migration_ptes(page + i, page + i, true);
+       }
 }
 
 static void __split_huge_page_tail(struct page *head, int tail,