]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/x86/mm/pageattr.c
x86, cpa: no need to check alias for __set_pages_p/__set_pages_np
[linux-2.6-omap-h63xx.git] / arch / x86 / mm / pageattr.c
index 4adb33628dec5608c8167c28181441a942ce3a00..162812b05d2826ff36c0ca4ad151e121b72fa7db 100644 (file)
  * The current flushing context - we pass it instead of 5 arguments:
  */
 struct cpa_data {
-       unsigned long   vaddr;
+       unsigned long   *vaddr;
        pgprot_t        mask_set;
        pgprot_t        mask_clr;
        int             numpages;
-       int             flushtlb;
+       int             flags;
        unsigned long   pfn;
        unsigned        force_split : 1;
+       int             curpage;
 };
 
+#define CPA_FLUSHTLB 1
+#define CPA_ARRAY 2
+
 #ifdef CONFIG_PROC_FS
 static unsigned long direct_pages_count[PG_LEVEL_NUM];
 
@@ -55,13 +59,19 @@ static void split_page_count(int level)
 
 int arch_report_meminfo(char *page)
 {
-       int n = sprintf(page, "DirectMap4k:  %8lu\n"
-                       "DirectMap2M:  %8lu\n",
-                       direct_pages_count[PG_LEVEL_4K],
-                       direct_pages_count[PG_LEVEL_2M]);
+       int n = sprintf(page, "DirectMap4k:  %8lu kB\n",
+                       direct_pages_count[PG_LEVEL_4K] << 2);
+#if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE)
+       n += sprintf(page + n, "DirectMap2M:  %8lu kB\n",
+                       direct_pages_count[PG_LEVEL_2M] << 11);
+#else
+       n += sprintf(page + n, "DirectMap4M:  %8lu kB\n",
+                       direct_pages_count[PG_LEVEL_2M] << 12);
+#endif
 #ifdef CONFIG_X86_64
-       n += sprintf(page + n, "DirectMap1G:  %8lu\n",
-                    direct_pages_count[PG_LEVEL_1G]);
+       if (direct_gbpages)
+               n += sprintf(page + n, "DirectMap1G:  %8lu kB\n",
+                       direct_pages_count[PG_LEVEL_1G] << 20);
 #endif
        return n;
 }
@@ -184,6 +194,41 @@ static void cpa_flush_range(unsigned long start, int numpages, int cache)
        }
 }
 
+static void cpa_flush_array(unsigned long *start, int numpages, int cache)
+{
+       unsigned int i, level;
+       unsigned long *addr;
+
+       BUG_ON(irqs_disabled());
+
+       on_each_cpu(__cpa_flush_range, NULL, 1);
+
+       if (!cache)
+               return;
+
+       /* 4M threshold */
+       if (numpages >= 1024) {
+               if (boot_cpu_data.x86_model >= 4)
+                       wbinvd();
+               return;
+       }
+       /*
+        * We only need to flush on one CPU,
+        * clflush is a MESI-coherent instruction that
+        * will cause all other CPUs to flush the same
+        * cachelines:
+        */
+       for (i = 0, addr = start; i < numpages; i++, addr++) {
+               pte_t *pte = lookup_address(*addr, &level);
+
+               /*
+                * Only flush present addresses:
+                */
+               if (pte && (pte_val(*pte) & _PAGE_PRESENT))
+                       clflush_cache_range((void *) *addr, PAGE_SIZE);
+       }
+}
+
 /*
  * Certain areas of memory on x86 require very specific protection flags,
  * for example the BIOS area or kernel text. Callers don't always get this
@@ -392,7 +437,7 @@ 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;
+               cpa->flags |= CPA_FLUSHTLB;
                do_split = 0;
        }
 
@@ -578,11 +623,16 @@ out_unlock:
 
 static int __change_page_attr(struct cpa_data *cpa, int primary)
 {
-       unsigned long address = cpa->vaddr;
+       unsigned long address;
        int do_split, err;
        unsigned int level;
        pte_t *kpte, old_pte;
 
+       if (cpa->flags & CPA_ARRAY)
+               address = cpa->vaddr[cpa->curpage];
+       else
+               address = *cpa->vaddr;
+
 repeat:
        kpte = lookup_address(address, &level);
        if (!kpte)
@@ -592,10 +642,9 @@ repeat:
        if (!pte_val(old_pte)) {
                if (!primary)
                        return 0;
-               printk(KERN_WARNING "CPA: called for zero pte. "
+               WARN(1, KERN_WARNING "CPA: called for zero pte. "
                       "vaddr = %lx cpa->vaddr = %lx\n", address,
-                      cpa->vaddr);
-               WARN_ON(1);
+                      *cpa->vaddr);
                return -EINVAL;
        }
 
@@ -621,7 +670,7 @@ repeat:
                 */
                if (pte_val(old_pte) != pte_val(new_pte)) {
                        set_pte_atomic(kpte, new_pte);
-                       cpa->flushtlb = 1;
+                       cpa->flags |= CPA_FLUSHTLB;
                }
                cpa->numpages = 1;
                return 0;
@@ -645,7 +694,7 @@ repeat:
         */
        err = split_large_page(kpte, address);
        if (!err) {
-               cpa->flushtlb = 1;
+               cpa->flags |= CPA_FLUSHTLB;
                goto repeat;
        }
 
@@ -658,6 +707,7 @@ static int cpa_process_alias(struct cpa_data *cpa)
 {
        struct cpa_data alias_cpa;
        int ret = 0;
+       unsigned long temp_cpa_vaddr, vaddr;
 
        if (cpa->pfn >= max_pfn_mapped)
                return 0;
@@ -670,16 +720,24 @@ static int cpa_process_alias(struct cpa_data *cpa)
         * No need to redo, when the primary call touched the direct
         * mapping already:
         */
-       if (!(within(cpa->vaddr, PAGE_OFFSET,
+       if (cpa->flags & CPA_ARRAY)
+               vaddr = cpa->vaddr[cpa->curpage];
+       else
+               vaddr = *cpa->vaddr;
+
+       if (!(within(vaddr, PAGE_OFFSET,
                    PAGE_OFFSET + (max_low_pfn_mapped << PAGE_SHIFT))
 #ifdef CONFIG_X86_64
-               || within(cpa->vaddr, PAGE_OFFSET + (1UL<<32),
+               || within(vaddr, PAGE_OFFSET + (1UL<<32),
                    PAGE_OFFSET + (max_pfn_mapped << PAGE_SHIFT))
 #endif
        )) {
 
                alias_cpa = *cpa;
-               alias_cpa.vaddr = (unsigned long) __va(cpa->pfn << PAGE_SHIFT);
+               temp_cpa_vaddr = (unsigned long) __va(cpa->pfn << PAGE_SHIFT);
+               alias_cpa.vaddr = &temp_cpa_vaddr;
+               alias_cpa.flags &= ~CPA_ARRAY;
+
 
                ret = __change_page_attr_set_clr(&alias_cpa, 0);
        }
@@ -691,7 +749,7 @@ static int cpa_process_alias(struct cpa_data *cpa)
         * No need to redo, when the primary call touched the high
         * mapping already:
         */
-       if (within(cpa->vaddr, (unsigned long) _text, (unsigned long) _end))
+       if (within(vaddr, (unsigned long) _text, (unsigned long) _end))
                return 0;
 
        /*
@@ -702,8 +760,9 @@ static int cpa_process_alias(struct cpa_data *cpa)
                return 0;
 
        alias_cpa = *cpa;
-       alias_cpa.vaddr =
-               (cpa->pfn << PAGE_SHIFT) + __START_KERNEL_map - phys_base;
+       temp_cpa_vaddr = (cpa->pfn << PAGE_SHIFT) + __START_KERNEL_map - phys_base;
+       alias_cpa.vaddr = &temp_cpa_vaddr;
+       alias_cpa.flags &= ~CPA_ARRAY;
 
        /*
         * The high mapping range is imprecise, so ignore the return value.
@@ -723,6 +782,9 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias)
                 * preservation check.
                 */
                cpa->numpages = numpages;
+               /* for array changes, we can't use large page */
+               if (cpa->flags & CPA_ARRAY)
+                       cpa->numpages = 1;
 
                ret = __change_page_attr(cpa, checkalias);
                if (ret)
@@ -741,7 +803,11 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias)
                 */
                BUG_ON(cpa->numpages > numpages);
                numpages -= cpa->numpages;
-               cpa->vaddr += cpa->numpages * PAGE_SIZE;
+               if (cpa->flags & CPA_ARRAY)
+                       cpa->curpage++;
+               else
+                       *cpa->vaddr += cpa->numpages * PAGE_SIZE;
+
        }
        return 0;
 }
@@ -752,12 +818,12 @@ static inline int cache_attr(pgprot_t attr)
                (_PAGE_PAT | _PAGE_PAT_LARGE | _PAGE_PWT | _PAGE_PCD);
 }
 
-static int do_change_page_attr_set_clr(unsigned long addr, int numpages,
+static int change_page_attr_set_clr(unsigned long *addr, int numpages,
                                    pgprot_t mask_set, pgprot_t mask_clr,
-                                   int force_split, int *tlb_flush)
+                                   int force_split, int array)
 {
        struct cpa_data cpa;
-       int ret, checkalias;
+       int ret, cache, checkalias;
 
        /*
         * Check, if we are requested to change a not supported
@@ -769,12 +835,22 @@ static int do_change_page_attr_set_clr(unsigned long addr, int numpages,
                return 0;
 
        /* Ensure we are PAGE_SIZE aligned */
-       if (addr & ~PAGE_MASK) {
-               addr &= PAGE_MASK;
-               /*
-                * People should not be passing in unaligned addresses:
-                */
-               WARN_ON_ONCE(1);
+       if (!array) {
+               if (*addr & ~PAGE_MASK) {
+                       *addr &= PAGE_MASK;
+                       /*
+                        * People should not be passing in unaligned addresses:
+                        */
+                       WARN_ON_ONCE(1);
+               }
+       } else {
+               int i;
+               for (i = 0; i < numpages; i++) {
+                       if (addr[i] & ~PAGE_MASK) {
+                               addr[i] &= PAGE_MASK;
+                               WARN_ON_ONCE(1);
+                       }
+               }
        }
 
        /* Must avoid aliasing mappings in the highmem code */
@@ -784,9 +860,13 @@ static int do_change_page_attr_set_clr(unsigned long addr, int numpages,
        cpa.numpages = numpages;
        cpa.mask_set = mask_set;
        cpa.mask_clr = mask_clr;
-       cpa.flushtlb = 0;
+       cpa.flags = 0;
+       cpa.curpage = 0;
        cpa.force_split = force_split;
 
+       if (array)
+               cpa.flags |= CPA_ARRAY;
+
        /* No alias checking for _NX bit modifications */
        checkalias = (pgprot_val(mask_set) | pgprot_val(mask_clr)) != _PAGE_NX;
 
@@ -795,22 +875,9 @@ static int do_change_page_attr_set_clr(unsigned long addr, int numpages,
        /*
         * Check whether we really changed something:
         */
-       *tlb_flush = cpa.flushtlb;
-       cpa_fill_pool(NULL);
-
-       return ret;
-}
-
-static int change_page_attr_set_clr(unsigned long addr, int numpages,
-                                   pgprot_t mask_set, pgprot_t mask_clr,
-                                   int force_split)
-{
-       int cache, flush_cache = 0, ret;
-
-       ret = do_change_page_attr_set_clr(addr, numpages, mask_set, mask_clr,
-               force_split, &flush_cache);
-       if (!flush_cache)
+       if (!(cpa.flags & CPA_FLUSHTLB))
                goto out;
+
        /*
         * No need to flush, when we did not set any of the caching
         * attributes:
@@ -823,24 +890,32 @@ static int change_page_attr_set_clr(unsigned long addr, int numpages,
         * error case we fall back to cpa_flush_all (which uses
         * wbindv):
         */
-       if (!ret && cpu_has_clflush)
-               cpa_flush_range(addr, numpages, cache);
-       else
+       if (!ret && cpu_has_clflush) {
+               if (cpa.flags & CPA_ARRAY)
+                       cpa_flush_array(addr, numpages, cache);
+               else
+                       cpa_flush_range(*addr, numpages, cache);
+       } else
                cpa_flush_all(cache);
+
 out:
+       cpa_fill_pool(NULL);
+
        return ret;
 }
 
-static inline int change_page_attr_set(unsigned long addr, int numpages,
-                                      pgprot_t mask)
+static inline int change_page_attr_set(unsigned long *addr, int numpages,
+                                      pgprot_t mask, int array)
 {
-       return change_page_attr_set_clr(addr, numpages, mask, __pgprot(0), 0);
+       return change_page_attr_set_clr(addr, numpages, mask, __pgprot(0), 0,
+               array);
 }
 
-static inline int change_page_attr_clear(unsigned long addr, int numpages,
-                                        pgprot_t mask)
+static inline int change_page_attr_clear(unsigned long *addr, int numpages,
+                                        pgprot_t mask, int array)
 {
-       return change_page_attr_set_clr(addr, numpages, __pgprot(0), mask, 0);
+       return change_page_attr_set_clr(addr, numpages, __pgprot(0), mask, 0,
+               array);
 }
 
 int _set_memory_uc(unsigned long addr, int numpages)
@@ -848,8 +923,8 @@ int _set_memory_uc(unsigned long addr, int numpages)
        /*
         * for now UC MINUS. see comments in ioremap_nocache()
         */
-       return change_page_attr_set(addr, numpages,
-                                   __pgprot(_PAGE_CACHE_UC_MINUS));
+       return change_page_attr_set(&addr, numpages,
+                                   __pgprot(_PAGE_CACHE_UC_MINUS), 0);
 }
 
 int set_memory_uc(unsigned long addr, int numpages)
@@ -857,7 +932,7 @@ int set_memory_uc(unsigned long addr, int numpages)
        /*
         * for now UC MINUS. see comments in ioremap_nocache()
         */
-       if (reserve_memtype(addr, addr + numpages * PAGE_SIZE,
+       if (reserve_memtype(__pa(addr), __pa(addr) + numpages * PAGE_SIZE,
                            _PAGE_CACHE_UC_MINUS, NULL))
                return -EINVAL;
 
@@ -865,34 +940,48 @@ int set_memory_uc(unsigned long addr, int numpages)
 }
 EXPORT_SYMBOL(set_memory_uc);
 
-int set_memory_uc_noflush(unsigned long addr, int numpages)
+int set_memory_array_uc(unsigned long *addr, int addrinarray)
 {
-       int flush;
-       /*
-        * for now UC MINUS. see comments in ioremap_nocache()
-        */
-       if (reserve_memtype(addr, addr + numpages * PAGE_SIZE,
-                           _PAGE_CACHE_UC_MINUS, NULL))
-               return -EINVAL;
+       unsigned long start;
+       unsigned long end;
+       int i;
        /*
         * for now UC MINUS. see comments in ioremap_nocache()
         */
-       return do_change_page_attr_set_clr(addr, numpages,
-                                   __pgprot(_PAGE_CACHE_UC_MINUS),
-                                       __pgprot(0), 0, &flush);
-}
-EXPORT_SYMBOL(set_memory_uc_noflush);
+       for (i = 0; i < addrinarray; i++) {
+               start = __pa(addr[i]);
+               for (end = start + PAGE_SIZE; i < addrinarray - 1; end += PAGE_SIZE) {
+                       if (end != __pa(addr[i + 1]))
+                               break;
+                       i++;
+               }
+               if (reserve_memtype(start, end, _PAGE_CACHE_UC_MINUS, NULL))
+                       goto out;
+       }
 
-void set_memory_flush_all(void)
-{
-       cpa_flush_all(1);
+       return change_page_attr_set(addr, addrinarray,
+                                   __pgprot(_PAGE_CACHE_UC_MINUS), 1);
+out:
+       for (i = 0; i < addrinarray; i++) {
+               unsigned long tmp = __pa(addr[i]);
+
+               if (tmp == start)
+                       break;
+               for (end = tmp + PAGE_SIZE; i < addrinarray - 1; end += PAGE_SIZE) {
+                       if (end != __pa(addr[i + 1]))
+                               break;
+                       i++;
+               }
+               free_memtype(tmp, end);
+       }
+       return -EINVAL;
 }
-EXPORT_SYMBOL(set_memory_flush_all);
+EXPORT_SYMBOL(set_memory_array_uc);
 
 int _set_memory_wc(unsigned long addr, int numpages)
 {
-       return change_page_attr_set(addr, numpages,
-                                   __pgprot(_PAGE_CACHE_WC));
+       return change_page_attr_set(&addr, numpages,
+                                   __pgprot(_PAGE_CACHE_WC), 0);
 }
 
 int set_memory_wc(unsigned long addr, int numpages)
@@ -900,7 +989,7 @@ int set_memory_wc(unsigned long addr, int numpages)
        if (!pat_enabled)
                return set_memory_uc(addr, numpages);
 
-       if (reserve_memtype(addr, addr + numpages * PAGE_SIZE,
+       if (reserve_memtype(__pa(addr), __pa(addr) + numpages * PAGE_SIZE,
                _PAGE_CACHE_WC, NULL))
                return -EINVAL;
 
@@ -910,49 +999,69 @@ EXPORT_SYMBOL(set_memory_wc);
 
 int _set_memory_wb(unsigned long addr, int numpages)
 {
-       return change_page_attr_clear(addr, numpages,
-                                     __pgprot(_PAGE_CACHE_MASK));
+       return change_page_attr_clear(&addr, numpages,
+                                     __pgprot(_PAGE_CACHE_MASK), 0);
 }
 
 int set_memory_wb(unsigned long addr, int numpages)
 {
-       free_memtype(addr, addr + numpages * PAGE_SIZE);
+       free_memtype(__pa(addr), __pa(addr) + numpages * PAGE_SIZE);
 
        return _set_memory_wb(addr, numpages);
 }
 EXPORT_SYMBOL(set_memory_wb);
 
+int set_memory_array_wb(unsigned long *addr, int addrinarray)
+{
+       int i;
+
+       for (i = 0; i < addrinarray; i++) {
+               unsigned long start = __pa(addr[i]);
+               unsigned long end;
+
+               for (end = start + PAGE_SIZE; i < addrinarray - 1; end += PAGE_SIZE) {
+                       if (end != __pa(addr[i + 1]))
+                               break;
+                       i++;
+               }
+               free_memtype(start, end);
+       }
+       return change_page_attr_clear(addr, addrinarray,
+                                     __pgprot(_PAGE_CACHE_MASK), 1);
+}
+EXPORT_SYMBOL(set_memory_array_wb);
+
 int set_memory_x(unsigned long addr, int numpages)
 {
-       return change_page_attr_clear(addr, numpages, __pgprot(_PAGE_NX));
+       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)
 {
-       return change_page_attr_set(addr, numpages, __pgprot(_PAGE_NX));
+       return change_page_attr_set(&addr, numpages, __pgprot(_PAGE_NX), 0);
 }
 EXPORT_SYMBOL(set_memory_nx);
 
 int set_memory_ro(unsigned long addr, int numpages)
 {
-       return change_page_attr_clear(addr, numpages, __pgprot(_PAGE_RW));
+       return change_page_attr_clear(&addr, numpages, __pgprot(_PAGE_RW), 0);
 }
 
 int set_memory_rw(unsigned long addr, int numpages)
 {
-       return change_page_attr_set(addr, numpages, __pgprot(_PAGE_RW));
+       return change_page_attr_set(&addr, numpages, __pgprot(_PAGE_RW), 0);
 }
 
 int set_memory_np(unsigned long addr, int numpages)
 {
-       return change_page_attr_clear(addr, numpages, __pgprot(_PAGE_PRESENT));
+       return change_page_attr_clear(&addr, numpages, __pgprot(_PAGE_PRESENT), 0);
 }
 
 int set_memory_4k(unsigned long addr, int numpages)
 {
-       return change_page_attr_set_clr(addr, numpages, __pgprot(0),
-                                       __pgprot(0), 1);
+       return change_page_attr_set_clr(&addr, numpages, __pgprot(0),
+                                       __pgprot(0), 1, 0);
 }
 
 int set_pages_uc(struct page *page, int numpages)
@@ -963,14 +1072,6 @@ int set_pages_uc(struct page *page, int numpages)
 }
 EXPORT_SYMBOL(set_pages_uc);
 
-int set_pages_uc_noflush(struct page *page, int numpages)
-{
-       unsigned long addr = (unsigned long)page_address(page);
-
-       return set_memory_uc_noflush(addr, numpages);
-}
-EXPORT_SYMBOL(set_pages_uc_noflush);
-
 int set_pages_wb(struct page *page, int numpages)
 {
        unsigned long addr = (unsigned long)page_address(page);
@@ -1013,22 +1114,38 @@ int set_pages_rw(struct page *page, int numpages)
 
 static int __set_pages_p(struct page *page, int numpages)
 {
-       struct cpa_data cpa = { .vaddr = (unsigned long) page_address(page),
+       unsigned long tempaddr = (unsigned long) page_address(page);
+       struct cpa_data cpa = { .vaddr = &tempaddr,
                                .numpages = numpages,
                                .mask_set = __pgprot(_PAGE_PRESENT | _PAGE_RW),
-                               .mask_clr = __pgprot(0)};
+                               .mask_clr = __pgprot(0),
+                               .flags = 0};
 
-       return __change_page_attr_set_clr(&cpa, 1);
+       /*
+        * No alias checking needed for setting present flag. otherwise,
+        * we may need to break large pages for 64-bit kernel text
+        * mappings (this adds to complexity if we want to do this from
+        * atomic context especially). Let's keep it simple!
+        */
+       return __change_page_attr_set_clr(&cpa, 0);
 }
 
 static int __set_pages_np(struct page *page, int numpages)
 {
-       struct cpa_data cpa = { .vaddr = (unsigned long) page_address(page),
+       unsigned long tempaddr = (unsigned long) page_address(page);
+       struct cpa_data cpa = { .vaddr = &tempaddr,
                                .numpages = numpages,
                                .mask_set = __pgprot(0),
-                               .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW)};
+                               .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW),
+                               .flags = 0};
 
-       return __change_page_attr_set_clr(&cpa, 1);
+       /*
+        * No alias checking needed for setting not present flag. otherwise,
+        * we may need to break large pages for 64-bit kernel text
+        * mappings (this adds to complexity if we want to do this from
+        * atomic context especially). Let's keep it simple!
+        */
+       return __change_page_attr_set_clr(&cpa, 0);
 }
 
 void kernel_map_pages(struct page *page, int numpages, int enable)
@@ -1048,11 +1165,8 @@ void kernel_map_pages(struct page *page, int numpages, int enable)
 
        /*
         * The return value is ignored as the calls cannot fail.
-        * Large pages are kept enabled at boot time, and are
-        * split up quickly with DEBUG_PAGEALLOC. If a splitup
-        * fails here (due to temporary memory shortage) no damage
-        * is done because we just keep the largepage intact up
-        * to the next attempt when it will likely be split up:
+        * Large pages for identity mappings are not used at boot time
+        * and hence no memory allocations during large page split.
         */
        if (enable)
                __set_pages_p(page, numpages);