include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / arch / x86 / mm / pageattr.c
index dce282f..28195c3 100644 (file)
@@ -6,13 +6,13 @@
 #include <linux/bootmem.h>
 #include <linux/module.h>
 #include <linux/sched.h>
-#include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
 #include <linux/pfn.h>
 #include <linux/percpu.h>
+#include <linux/gfp.h>
 
 #include <asm/e820.h>
 #include <asm/processor.h>
@@ -144,6 +144,7 @@ void clflush_cache_range(void *vaddr, unsigned int size)
 
        mb();
 }
+EXPORT_SYMBOL_GPL(clflush_cache_range);
 
 static void __cpa_flush_all(void *arg)
 {
@@ -278,6 +279,43 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
                   __pa((unsigned long)__end_rodata) >> PAGE_SHIFT))
                pgprot_val(forbidden) |= _PAGE_RW;
 
+#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
+       /*
+        * Once the kernel maps the text as RO (kernel_set_to_readonly is set),
+        * kernel text mappings for the large page aligned text, rodata sections
+        * will be always read-only. For the kernel identity mappings covering
+        * the holes caused by this alignment can be anything that user asks.
+        *
+        * This will preserve the large page mappings for kernel text/data
+        * at no extra cost.
+        */
+       if (kernel_set_to_readonly &&
+           within(address, (unsigned long)_text,
+                  (unsigned long)__end_rodata_hpage_align)) {
+               unsigned int level;
+
+               /*
+                * Don't enforce the !RW mapping for the kernel text mapping,
+                * if the current mapping is already using small page mapping.
+                * No need to work hard to preserve large page mappings in this
+                * case.
+                *
+                * This also fixes the Linux Xen paravirt guest boot failure
+                * (because of unexpected read-only mappings for kernel identity
+                * mappings). In this paravirt guest case, the kernel text
+                * mapping and the kernel identity mapping share the same
+                * page-table pages. Thus we can't really use different
+                * protections for the kernel text and identity mappings. Also,
+                * these shared mappings are made of small page mappings.
+                * Thus this don't enforce !RW mapping for small page kernel
+                * text mapping logic will help Linux Xen parvirt guest boot
+                * aswell.
+                */
+               if (lookup_address(address, &level) && (level != PG_LEVEL_4K))
+                       pgprot_val(forbidden) |= _PAGE_RW;
+       }
+#endif
+
        prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden));
 
        return prot;
@@ -687,7 +725,7 @@ static int cpa_process_alias(struct cpa_data *cpa)
 {
        struct cpa_data alias_cpa;
        unsigned long laddr = (unsigned long)__va(cpa->pfn << PAGE_SHIFT);
-       unsigned long vaddr, remapped;
+       unsigned long vaddr;
        int ret;
 
        if (cpa->pfn >= max_pfn_mapped)
@@ -745,24 +783,6 @@ static int cpa_process_alias(struct cpa_data *cpa)
        }
 #endif
 
-       /*
-        * If the PMD page was partially used for per-cpu remapping,
-        * the recycled area needs to be split and modified.  Because
-        * the area is always proper subset of a PMD page
-        * cpa->numpages is guaranteed to be 1 for these areas, so
-        * there's no need to loop over and check for further remaps.
-        */
-       remapped = (unsigned long)pcpu_lpage_remapped((void *)laddr);
-       if (remapped) {
-               WARN_ON(cpa->numpages > 1);
-               alias_cpa = *cpa;
-               alias_cpa.vaddr = &remapped;
-               alias_cpa.flags &= ~(CPA_PAGES_ARRAY | CPA_ARRAY);
-               ret = __change_page_attr_set_clr(&alias_cpa, 0);
-               if (ret)
-                       return ret;
-       }
-
        return 0;
 }
 
@@ -823,6 +843,7 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
 {
        struct cpa_data cpa;
        int ret, cache, checkalias;
+       unsigned long baddr = 0;
 
        /*
         * Check, if we are requested to change a not supported
@@ -854,6 +875,11 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
                         */
                        WARN_ON_ONCE(1);
                }
+               /*
+                * Save address for cache flush. *addr is modified in the call
+                * to __change_page_attr_set_clr() below.
+                */
+               baddr = *addr;
        }
 
        /* Must avoid aliasing mappings in the highmem code */
@@ -901,7 +927,7 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
                        cpa_flush_array(addr, numpages, cache,
                                        cpa.flags, pages);
                } else
-                       cpa_flush_range(*addr, numpages, cache);
+                       cpa_flush_range(baddr, numpages, cache);
        } else
                cpa_flush_all(cache);
 
@@ -1080,12 +1106,18 @@ EXPORT_SYMBOL(set_memory_array_wb);
 
 int set_memory_x(unsigned long addr, int numpages)
 {
+       if (!(__supported_pte_mask & _PAGE_NX))
+               return 0;
+
        return change_page_attr_clear(&addr, numpages, __pgprot(_PAGE_NX), 0);
 }
 EXPORT_SYMBOL(set_memory_x);
 
 int set_memory_nx(unsigned long addr, int numpages)
 {
+       if (!(__supported_pte_mask & _PAGE_NX))
+               return 0;
+
        return change_page_attr_set(&addr, numpages, __pgprot(_PAGE_NX), 0);
 }
 EXPORT_SYMBOL(set_memory_nx);