return entry;
 }
 
+static void clear_flush(struct mm_struct *mm,
+                       unsigned long addr,
+                       pte_t *ptep,
+                       unsigned long pgsize,
+                       unsigned long ncontig)
+{
+       struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0);
+       unsigned long i, saddr = addr;
+
+       for (i = 0; i < ncontig; i++, addr += pgsize, ptep++)
+               ptep_get_and_clear(mm, addr, ptep);
+
+       flush_tlb_range(&vma, saddr, addr);
+}
+
+/*
+ * When dealing with NAPOT mappings, the privileged specification indicates that
+ * "if an update needs to be made, the OS generally should first mark all of the
+ * PTEs invalid, then issue SFENCE.VMA instruction(s) covering all 4 KiB regions
+ * within the range, [...] then update the PTE(s), as described in Section
+ * 4.2.1.". That's the equivalent of the Break-Before-Make approach used by
+ * arm64.
+ */
 void set_huge_pte_at(struct mm_struct *mm,
                     unsigned long addr,
                     pte_t *ptep,
                     pte_t pte,
                     unsigned long sz)
 {
-       unsigned long hugepage_shift;
+       unsigned long hugepage_shift, pgsize;
        int i, pte_num;
 
        if (sz >= PGDIR_SIZE)
                hugepage_shift = PAGE_SHIFT;
 
        pte_num = sz >> hugepage_shift;
-       for (i = 0; i < pte_num; i++, ptep++, addr += (1 << hugepage_shift))
+       pgsize = 1 << hugepage_shift;
+
+       if (!pte_present(pte)) {
+               for (i = 0; i < pte_num; i++, ptep++, addr += pgsize)
+                       set_ptes(mm, addr, ptep, pte, 1);
+               return;
+       }
+
+       if (!pte_napot(pte)) {
+               set_ptes(mm, addr, ptep, pte, 1);
+               return;
+       }
+
+       clear_flush(mm, addr, ptep, pgsize, pte_num);
+
+       for (i = 0; i < pte_num; i++, ptep++, addr += pgsize)
                set_pte_at(mm, addr, ptep, pte);
 }