]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/x86/mm/pageattr.c
x86: remove suprious ifdefs from pageattr.c
[linux-2.6-omap-h63xx.git] / arch / x86 / mm / pageattr.c
index 3810f7a83b1da62caee23d5a8d8c564264e2ede7..8493c855582bf56a5a36d2e6e64266332c9d26c0 100644 (file)
@@ -16,6 +16,9 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 
+/*
+ * The current flushing context - we pass it instead of 5 arguments:
+ */
 struct cpa_data {
        unsigned long   vaddr;
        pgprot_t        mask_set;
@@ -24,11 +27,6 @@ struct cpa_data {
        int             flushtlb;
 };
 
-enum {
-       CPA_NO_SPLIT = 0,
-       CPA_SPLIT,
-};
-
 static inline int
 within(unsigned long addr, unsigned long start, unsigned long end)
 {
@@ -119,7 +117,7 @@ static void cpa_flush_range(unsigned long start, int numpages, int cache)
                /*
                 * Only flush present addresses:
                 */
-               if (pte && pte_present(*pte))
+               if (pte && (pte_val(*pte) & _PAGE_PRESENT))
                        clflush_cache_range((void *) addr, PAGE_SIZE);
        }
 }
@@ -169,8 +167,6 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address)
        if (within(address, virt_to_highmap(_text), virt_to_highmap(_etext)))
                pgprot_val(forbidden) |= _PAGE_NX;
 
-
-#ifdef CONFIG_DEBUG_RODATA
        /* The .rodata section needs to be read-only */
        if (within(address, (unsigned long)__start_rodata,
                                (unsigned long)__end_rodata))
@@ -181,13 +177,20 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address)
        if (within(address, virt_to_highmap(__start_rodata),
                                virt_to_highmap(__end_rodata)))
                pgprot_val(forbidden) |= _PAGE_RW;
-#endif
 
        prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden));
 
        return prot;
 }
 
+/*
+ * Lookup the page table entry for a virtual address. Return a pointer
+ * to the entry and the level of the mapping.
+ *
+ * Note: We return pud and pmd either when the entry is marked large
+ * or when the present bit is not set. Otherwise we would return a
+ * pointer to a nonexisting mapping.
+ */
 pte_t *lookup_address(unsigned long address, int *level)
 {
        pgd_t *pgd = pgd_offset_k(address);
@@ -198,21 +201,31 @@ pte_t *lookup_address(unsigned long address, int *level)
 
        if (pgd_none(*pgd))
                return NULL;
+
        pud = pud_offset(pgd, address);
        if (pud_none(*pud))
                return NULL;
+
+       *level = PG_LEVEL_1G;
+       if (pud_large(*pud) || !pud_present(*pud))
+               return (pte_t *)pud;
+
        pmd = pmd_offset(pud, address);
        if (pmd_none(*pmd))
                return NULL;
 
        *level = PG_LEVEL_2M;
-       if (pmd_large(*pmd))
+       if (pmd_large(*pmd) || !pmd_present(*pmd))
                return (pte_t *)pmd;
 
        *level = PG_LEVEL_4K;
+
        return pte_offset_kernel(pmd, address);
 }
 
+/*
+ * Set the new pmd in all the pgds we know about:
+ */
 static void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte)
 {
        /* change init_mm */
@@ -235,24 +248,14 @@ static void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte)
 #endif
 }
 
-static int try_preserve_large_page(pte_t *kpte, unsigned long address,
-                                  struct cpa_data *cpa)
+static int
+try_preserve_large_page(pte_t *kpte, unsigned long address,
+                       struct cpa_data *cpa)
 {
        unsigned long nextpage_addr, numpages, pmask, psize, flags;
        pte_t new_pte, old_pte, *tmp;
        pgprot_t old_prot, new_prot;
-       int level, res = CPA_SPLIT;
-
-       /*
-        * An Athlon 64 X2 showed hard hangs if we tried to preserve
-        * largepages and changed the PSE entry from RW to RO.
-        *
-        * As AMD CPUs have a long series of erratas in this area,
-        * (and none of the known ones seem to explain this hang),
-        * disable this code until the hang can be debugged:
-        */
-       if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
-               return res;
+       int level, do_split = 1;
 
        spin_lock_irqsave(&pgd_lock, flags);
        /*
@@ -265,12 +268,17 @@ static int try_preserve_large_page(pte_t *kpte, unsigned long address,
 
        switch (level) {
        case PG_LEVEL_2M:
-               psize = LARGE_PAGE_SIZE;
-               pmask = LARGE_PAGE_MASK;
+               psize = PMD_PAGE_SIZE;
+               pmask = PMD_PAGE_MASK;
                break;
+#ifdef CONFIG_X86_64
        case PG_LEVEL_1G:
+               psize = PMD_PAGE_SIZE;
+               pmask = PMD_PAGE_MASK;
+               break;
+#endif
        default:
-               res = -EINVAL;
+               do_split = -EINVAL;
                goto out_unlock;
        }
 
@@ -298,7 +306,7 @@ static int try_preserve_large_page(pte_t *kpte, unsigned long address,
         * above:
         */
        if (pgprot_val(new_prot) == pgprot_val(old_prot)) {
-               res = CPA_NO_SPLIT;
+               do_split = 0;
                goto out_unlock;
        }
 
@@ -318,25 +326,25 @@ static int try_preserve_large_page(pte_t *kpte, unsigned long address,
                new_pte = pfn_pte(pte_pfn(old_pte), canon_pgprot(new_prot));
                __set_pmd_pte(kpte, address, new_pte);
                cpa->flushtlb = 1;
-               res = CPA_NO_SPLIT;
+               do_split = 0;
        }
 
 out_unlock:
        spin_unlock_irqrestore(&pgd_lock, flags);
-       return res;
+
+       return do_split;
 }
 
 static int split_large_page(pte_t *kpte, unsigned long address)
 {
-       pgprot_t ref_prot = pte_pgprot(pte_clrhuge(*kpte));
+       unsigned long flags, pfn, pfninc = 1;
        gfp_t gfp_flags = GFP_KERNEL;
-       unsigned long flags, addr, pfn;
+       unsigned int i, level;
        pte_t *pbase, *tmp;
+       pgprot_t ref_prot;
        struct page *base;
-       unsigned int i, level;
 
 #ifdef CONFIG_DEBUG_PAGEALLOC
-       gfp_flags = __GFP_HIGH | __GFP_NOFAIL | __GFP_NOWARN;
        gfp_flags = GFP_ATOMIC | __GFP_NOWARN;
 #endif
        base = alloc_pages(gfp_flags, 0);
@@ -349,33 +357,41 @@ static int split_large_page(pte_t *kpte, unsigned long address)
         * up for us already:
         */
        tmp = lookup_address(address, &level);
-       if (tmp != kpte) {
-               WARN_ON_ONCE(1);
+       if (tmp != kpte)
                goto out_unlock;
-       }
 
-       address = __pa(address);
-       addr = address & LARGE_PAGE_MASK;
        pbase = (pte_t *)page_address(base);
 #ifdef CONFIG_X86_32
        paravirt_alloc_pt(&init_mm, page_to_pfn(base));
 #endif
+       ref_prot = pte_pgprot(pte_clrhuge(*kpte));
+
+#ifdef CONFIG_X86_64
+       if (level == PG_LEVEL_1G) {
+               pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT;
+               pgprot_val(ref_prot) |= _PAGE_PSE;
+       }
+#endif
 
        /*
         * Get the target pfn from the original entry:
         */
        pfn = pte_pfn(*kpte);
-       for (i = 0; i < PTRS_PER_PTE; i++, pfn++)
+       for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc)
                set_pte(&pbase[i], pfn_pte(pfn, ref_prot));
 
        /*
-        * Install the new, split up pagetable. Important detail here:
+        * Install the new, split up pagetable. Important details here:
         *
         * On Intel the NX bit of all levels must be cleared to make a
         * page executable. See section 4.13.2 of Intel 64 and IA-32
         * Architectures Software Developer's Manual).
+        *
+        * Mark the entry present. The current mapping might be
+        * set to not present, which we preserved above.
         */
        ref_prot = pte_pgprot(pte_mkexec(pte_clrhuge(*kpte)));
+       pgprot_val(ref_prot) |= _PAGE_PRESENT;
        __set_pmd_pte(kpte, address, mk_pte(base, ref_prot));
        base = NULL;
 
@@ -390,8 +406,8 @@ out_unlock:
 
 static int __change_page_attr(unsigned long address, struct cpa_data *cpa)
 {
+       int level, do_split, err;
        struct page *kpte_page;
-       int level, res;
        pte_t *kpte;
 
 repeat:
@@ -442,26 +458,25 @@ repeat:
         * Check, whether we can keep the large page intact
         * and just change the pte:
         */
-       res = try_preserve_large_page(kpte, address, cpa);
-       if (res < 0)
-               return res;
-
+       do_split = try_preserve_large_page(kpte, address, cpa);
        /*
         * When the range fits into the existing large page,
         * return. cp->numpages and cpa->tlbflush have been updated in
         * try_large_page:
         */
-       if (res == CPA_NO_SPLIT)
-               return 0;
+       if (do_split <= 0)
+               return do_split;
 
        /*
         * We have to split the large page:
         */
-       res = split_large_page(kpte, address);
-       if (res)
-               return res;
-       cpa->flushtlb = 1;
-       goto repeat;
+       err = split_large_page(kpte, address);
+       if (!err) {
+               cpa->flushtlb = 1;
+               goto repeat;
+       }
+
+       return err;
 }
 
 /**
@@ -477,7 +492,6 @@ repeat:
  *
  * Modules and drivers should use the set_memory_* APIs instead.
  */
-
 static int change_page_attr_addr(struct cpa_data *cpa)
 {
        int err;