diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/include/asm/pgtable-ppc64.h
index 43e6ad424c7f..bb654c192028 100644
--- a/arch/powerpc/include/asm/pgtable-ppc64.h
+++ b/arch/powerpc/include/asm/pgtable-ppc64.h
@@ -506,6 +506,22 @@ static inline pmd_t pmd_mkhuge(pmd_t pmd)
return pmd;
}
+#define pte_mkprotnone pte_mkprotnone
+static inline pte_t pte_mkprotnone(pte_t pte)
+{
+ pte_val(pte) &= ~_PAGE_PRESENT;
+ pte_val(pte) |= _PAGE_USER;
+ return pte;
+}
+
+#define pmd_mkprotnone pmd_mkprotnone
+static inline pmd_t pmd_mkprotnone(pmd_t pmd)
+{
+ pmd_val(pmd) &= ~_PAGE_PRESENT;
+ pmd_val(pmd) |= _PAGE_USER;
+ return pmd;
+}
+
static inline pmd_t pmd_mknotpresent(pmd_t pmd)
{
pmd_val(pmd) &= ~_PAGE_PRESENT;diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index a0c35bf6cb92..5524fa6f7e8e 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -292,6 +292,20 @@ static inline pmd_t pmd_mkwrite(pmd_t pmd)
return pmd_set_flags(pmd, _PAGE_RW);
}
+#define pte_mkprotnone pte_mkprotnone
+static inline pte_t pte_mkprotnone(pte_t pte)
+{
+ pte = pte_clear_flags(pte, _PAGE_PRESENT);
+ return pte_set_flags(pte, _PAGE_PROTNONE);
+}
+
+#define pmd_mkprotnone pmd_mkprotnone
+static inline pmd_t pmd_mkprotnone(pmd_t pmd)
+{
+ pmd = pmd_clear_flags(pmd, _PAGE_PRESENT);
+ return pmd_set_flags(pmd, _PAGE_PROTNONE);
+}
+
static inline pmd_t pmd_mknotpresent(pmd_t pmd)
{
return pmd_clear_flags(pmd, _PAGE_PRESENT | _PAGE_PROTNONE);diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index 4d46085c1b90..837dbde26662 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -669,6 +669,25 @@ static inline int pmd_trans_unstable(pmd_t *pmd)
#endif
}
+#ifndef pte_mkprotnone
+/*
+ * Only automatic NUMA balancing needs this so arches that support it must
+ * define pte_mknotpresent.
+ */
+static inline pte_mkprotnone(pte_t pte)
+{
+ BUG();
+}
+#endif
+
+#ifndef pmd_mkprotnone
+static inline pmd_mkprotnone(pmd_t pmd)
+{
+ BUG();
+}
+#endif
+
+
#ifndef CONFIG_NUMA_BALANCING
/*
* Technically a PTE can be PROTNONE even when not doing NUMA balancing butdiff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 194c0f019774..d9d5a2045d10 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1495,11 +1495,24 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
}
if (!prot_numa || !pmd_protnone(*pmd)) {
- entry = pmdp_get_and_clear_notify(mm, addr, pmd);
- entry = pmd_modify(entry, newprot);
+ /*
+ * NUMA hinting update can avoid a clear and defer the
+ * flush as it is not a functional correctness issue if
+ * access occurs after the update and this avoids
+ * spurious faults.
+ */
+ if (prot_numa) {
+ entry = *pmd;
+ entry = pmd_mkprotnone(entry);
+ } else {
+ entry = pmdp_get_and_clear_notify(mm, addr,
+ pmd);
+ entry = pmd_modify(entry, newprot);
+ BUG_ON(pmd_write(entry));
+ }
+
ret = HPAGE_PMD_NR;
set_pmd_at(mm, addr, pmd, entry);
- BUG_ON(pmd_write(entry));
}
spin_unlock(ptl);
}diff --git a/mm/mprotect.c b/mm/mprotect.c
index 44727811bf4c..aa97d5cab6ce 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -90,6 +90,11 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
/* Avoid TLB flush if possible */
if (pte_protnone(oldpte))
continue;
+
+ ptent = pte_mkprotnone(oldpte);
+ set_pte_at(mm, addr, pte, ptent);
+ pages++;
+ continue;
}
ptent = ptep_modify_prot_start(mm, addr, pte);
--
2.1.2