The range check whether the address is aligned to the large page and
covers the full large page (1G or 2M) is obvious to do _before_
static_protection() check, because if the requested range does not fit
and has a different pgprot_val() then it will decide to split after the
check anyway.

The approach and some of the comments came from Thomas Gleixner's
email example for how to do this

Suggested-by: Thomas Gleixner <t...@linutronix.de>
Signed-off-by: Bin Yang <bin.y...@intel.com>
---
 arch/x86/mm/pageattr.c | 35 ++++++++++++++++-------------------
 1 file changed, 16 insertions(+), 19 deletions(-)

diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 68613fd..091f1d3 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -645,11 +645,21 @@ try_preserve_large_page(pte_t *kpte, unsigned long 
address,
        }
 
        /*
+        * If the requested address range is not aligned to the start of
+        * the large page or does not cover the full range, split it up.
+        * No matter what the static_protections() check below does, it
+        * would anyway result in a split after doing all the check work
+        * for nothing.
+        */
+       addr = address & pmask;
+       if (address != addr || cpa->numpages != numpages)
+               goto out_unlock;
+
+       /*
         * We need to check the full range, whether
         * static_protection() requires a different pgprot for one of
         * the pages in the range we try to preserve:
         */
-       addr = address & pmask;
        pfn = old_pfn;
        for (i = 0; i < (psize >> PAGE_SHIFT); i++, addr += PAGE_SIZE, pfn++) {
                pgprot_t chk_prot = static_protections(req_prot, addr, pfn);
@@ -659,24 +669,11 @@ try_preserve_large_page(pte_t *kpte, unsigned long 
address,
        }
 
 
-       /*
-        * We need to change the attributes. Check, whether we can
-        * change the large page in one go. We request a split, when
-        * the address is not aligned and the number of pages is
-        * smaller than the number of pages in the large page. Note
-        * that we limited the number of possible pages already to
-        * the number of pages in the large page.
-        */
-       if (address == (address & pmask) && cpa->numpages == (psize >> 
PAGE_SHIFT)) {
-               /*
-                * The address is aligned and the number of pages
-                * covers the full page.
-                */
-               new_pte = pfn_pte(old_pfn, new_prot);
-               __set_pmd_pte(kpte, address, new_pte);
-               cpa->flags |= CPA_FLUSHTLB;
-               do_split = 0;
-       }
+       /* All checks passed. Just change the large mapping entry */
+       new_pte = pfn_pte(old_pfn, new_prot);
+       __set_pmd_pte(kpte, address, new_pte);
+       cpa->flags |= CPA_FLUSHTLB;
+       do_split = 0;
 
 out_unlock:
        spin_unlock(&pgd_lock);
-- 
2.7.4

Reply via email to