bool is_hugetlb_entry_migration(pte_t pte);
 bool is_hugetlb_entry_hwpoisoned(pte_t pte);
 void hugetlb_unshare_all_pmds(struct vm_area_struct *vma);
+void fixup_hugetlb_reservations(struct vm_area_struct *vma);
 
 #else /* !CONFIG_HUGETLB_PAGE */
 
 
 static inline void hugetlb_unshare_all_pmds(struct vm_area_struct *vma) { }
 
+static inline void fixup_hugetlb_reservations(struct vm_area_struct *vma)
+{
+}
+
 #endif /* !CONFIG_HUGETLB_PAGE */
 
 #ifndef pgd_write
 
 /*
  * Reset and decrement one ref on hugepage private reservation.
  * Called with mm->mmap_lock writer semaphore held.
- * This function should be only used by move_vma() and operate on
+ * This function should be only used by mremap and operate on
  * same sized vma. It should never come here with last ref on the
  * reservation.
  */
        hugetlb_unshare_pmds(vma, ALIGN(vma->vm_start, PUD_SIZE),
                        ALIGN_DOWN(vma->vm_end, PUD_SIZE));
 }
+
+/*
+ * For hugetlb, mremap() is an odd edge case - while the VMA copying is
+ * performed, we permit both the old and new VMAs to reference the same
+ * reservation.
+ *
+ * We fix this up after the operation succeeds, or if a newly allocated VMA
+ * is closed as a result of a failure to allocate memory.
+ */
+void fixup_hugetlb_reservations(struct vm_area_struct *vma)
+{
+       if (is_vm_hugetlb_page(vma))
+               clear_vma_resv_huge_pages(vma);
+}
 
                mremap_userfaultfd_prep(new_vma, vrm->uf);
        }
 
-       if (is_vm_hugetlb_page(vma))
-               clear_vma_resv_huge_pages(vma);
+       fixup_hugetlb_reservations(vma);
 
        /* Tell pfnmap has moved from this vma */
        if (unlikely(vma->vm_flags & VM_PFNMAP))
 
        return new_vma;
 
 out_vma_link:
+       fixup_hugetlb_reservations(new_vma);
        vma_close(new_vma);
 
        if (new_vma->vm_file)