Merge branch 'linus' into cont_syslog
[safe/jmp/linux-2.6] / arch / powerpc / mm / hugetlbpage.c
index 95220a5..9bb249c 100644 (file)
@@ -7,29 +7,18 @@
  * Copyright (C) 2002, Rohit Seth <rohit.seth@intel.com>
  */
 
-#include <linux/init.h>
-#include <linux/fs.h>
 #include <linux/mm.h>
-#include <linux/hugetlb.h>
-#include <linux/pagemap.h>
+#include <linux/io.h>
 #include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/sysctl.h>
-#include <asm/mman.h>
+#include <linux/hugetlb.h>
+#include <asm/pgtable.h>
 #include <asm/pgalloc.h>
 #include <asm/tlb.h>
-#include <asm/tlbflush.h>
-#include <asm/mmu_context.h>
-#include <asm/machdep.h>
-#include <asm/cputable.h>
-#include <asm/spu.h>
 
 #define PAGE_SHIFT_64K 16
 #define PAGE_SHIFT_16M 24
 #define PAGE_SHIFT_16G 34
 
-#define NUM_LOW_AREAS  (0x100000000UL >> SID_SHIFT)
-#define NUM_HIGH_AREAS (PGTABLE_RANGE >> HTLB_AREA_SHIFT)
 #define MAX_NUMBER_GPAGES      1024
 
 /* Tracks the 16G pages after the device tree is scanned and before the
 static unsigned long gpage_freearray[MAX_NUMBER_GPAGES];
 static unsigned nr_gpages;
 
-/* Array of valid huge page sizes - non-zero value(hugepte_shift) is
- * stored for the huge page sizes that are valid.
- */
-static unsigned int mmu_huge_psizes[MMU_PAGE_COUNT] = { }; /* initialize all to 0 */
-
 /* Flag to mark huge PD pointers.  This means pmd_bad() and pud_bad()
  * will choke on pointers to hugepte tables, which is handy for
  * catching screwups early. */
 
 static inline int shift_to_mmu_psize(unsigned int shift)
 {
-       switch (shift) {
-#ifndef CONFIG_PPC_64K_PAGES
-       case PAGE_SHIFT_64K:
-           return MMU_PAGE_64K;
-#endif
-       case PAGE_SHIFT_16M:
-           return MMU_PAGE_16M;
-       case PAGE_SHIFT_16G:
-           return MMU_PAGE_16G;
-       }
+       int psize;
+
+       for (psize = 0; psize < MMU_PAGE_COUNT; ++psize)
+               if (mmu_psize_defs[psize].shift == shift)
+                       return psize;
        return -1;
 }
 
@@ -366,27 +345,6 @@ void hugetlb_free_pgd_range(struct mmu_gather *tlb,
        } while (pgd++, addr = next, addr != end);
 }
 
-void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
-                    pte_t *ptep, pte_t pte)
-{
-       if (pte_present(*ptep)) {
-               /* We open-code pte_clear because we need to pass the right
-                * argument to hpte_need_flush (huge / !huge). Might not be
-                * necessary anymore if we make hpte_need_flush() get the
-                * page size from the slices
-                */
-               pte_update(mm, addr, ptep, ~0UL, 1);
-       }
-       *ptep = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS);
-}
-
-pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
-                             pte_t *ptep)
-{
-       unsigned long old = pte_update(mm, addr, ptep, ~0UL, 1);
-       return __pte(old);
-}
-
 struct page *
 follow_huge_addr(struct mm_struct *mm, unsigned long address, int write)
 {
@@ -479,18 +437,27 @@ static noinline int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long add
        return 1;
 }
 
+static unsigned long hugepte_addr_end(unsigned long addr, unsigned long end,
+                                     unsigned long sz)
+{
+       unsigned long __boundary = (addr + sz) & ~(sz-1);
+       return (__boundary - 1 < end - 1) ? __boundary : end;
+}
+
 int gup_hugepd(hugepd_t *hugepd, unsigned pdshift,
               unsigned long addr, unsigned long end,
               int write, struct page **pages, int *nr)
 {
        pte_t *ptep;
        unsigned long sz = 1UL << hugepd_shift(*hugepd);
+       unsigned long next;
 
        ptep = hugepte_offset(hugepd, addr, pdshift);
        do {
+               next = hugepte_addr_end(addr, end, sz);
                if (!gup_hugepte(ptep, sz, addr, end, write, pages, nr))
                        return 0;
-       } while (ptep++, addr += sz, addr != end);
+       } while (ptep++, addr = next, addr != end);
 
        return 1;
 }
@@ -502,8 +469,6 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
        struct hstate *hstate = hstate_file(file);
        int mmu_psize = shift_to_mmu_psize(huge_page_shift(hstate));
 
-       if (!mmu_huge_psizes[mmu_psize])
-               return -EINVAL;
        return slice_get_unmapped_area(addr, len, flags, mmu_psize, 1, 0);
 }
 
@@ -514,199 +479,46 @@ unsigned long vma_mmu_pagesize(struct vm_area_struct *vma)
        return 1UL << mmu_psize_to_shift(psize);
 }
 
-/*
- * Called by asm hashtable.S for doing lazy icache flush
- */
-static unsigned int hash_huge_page_do_lazy_icache(unsigned long rflags,
-                                       pte_t pte, int trap, unsigned long sz)
-{
-       struct page *page;
-       int i;
-
-       if (!pfn_valid(pte_pfn(pte)))
-               return rflags;
-
-       page = pte_page(pte);
-
-       /* page is dirty */
-       if (!test_bit(PG_arch_1, &page->flags) && !PageReserved(page)) {
-               if (trap == 0x400) {
-                       for (i = 0; i < (sz / PAGE_SIZE); i++)
-                               __flush_dcache_icache(page_address(page+i));
-                       set_bit(PG_arch_1, &page->flags);
-               } else {
-                       rflags |= HPTE_R_N;
-               }
-       }
-       return rflags;
-}
-
-int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
-                    pte_t *ptep, unsigned long trap, int local, int ssize,
-                    unsigned int shift, unsigned int mmu_psize)
+static int __init add_huge_page_size(unsigned long long size)
 {
-       unsigned long old_pte, new_pte;
-       unsigned long va, rflags, pa, sz;
-       long slot;
-       int err = 1;
-
-       BUG_ON(shift != mmu_psize_defs[mmu_psize].shift);
-
-       /* Search the Linux page table for a match with va */
-       va = hpt_va(ea, vsid, ssize);
-
-       /* 
-        * Check the user's access rights to the page.  If access should be
-        * prevented then send the problem up to do_page_fault.
-        */
-       if (unlikely(access & ~pte_val(*ptep)))
-               goto out;
-       /*
-        * At this point, we have a pte (old_pte) which can be used to build
-        * or update an HPTE. There are 2 cases:
-        *
-        * 1. There is a valid (present) pte with no associated HPTE (this is 
-        *      the most common case)
-        * 2. There is a valid (present) pte with an associated HPTE. The
-        *      current values of the pp bits in the HPTE prevent access
-        *      because we are doing software DIRTY bit management and the
-        *      page is currently not DIRTY. 
-        */
-
-
-       do {
-               old_pte = pte_val(*ptep);
-               if (old_pte & _PAGE_BUSY)
-                       goto out;
-               new_pte = old_pte | _PAGE_BUSY | _PAGE_ACCESSED;
-       } while(old_pte != __cmpxchg_u64((unsigned long *)ptep,
-                                        old_pte, new_pte));
-
-       rflags = 0x2 | (!(new_pte & _PAGE_RW));
-       /* _PAGE_EXEC -> HW_NO_EXEC since it's inverted */
-       rflags |= ((new_pte & _PAGE_EXEC) ? 0 : HPTE_R_N);
-       sz = ((1UL) << shift);
-       if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
-               /* No CPU has hugepages but lacks no execute, so we
-                * don't need to worry about that case */
-               rflags = hash_huge_page_do_lazy_icache(rflags, __pte(old_pte),
-                                                      trap, sz);
-
-       /* Check if pte already has an hpte (case 2) */
-       if (unlikely(old_pte & _PAGE_HASHPTE)) {
-               /* There MIGHT be an HPTE for this pte */
-               unsigned long hash, slot;
-
-               hash = hpt_hash(va, shift, ssize);
-               if (old_pte & _PAGE_F_SECOND)
-                       hash = ~hash;
-               slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
-               slot += (old_pte & _PAGE_F_GIX) >> 12;
-
-               if (ppc_md.hpte_updatepp(slot, rflags, va, mmu_psize,
-                                        ssize, local) == -1)
-                       old_pte &= ~_PAGE_HPTEFLAGS;
-       }
-
-       if (likely(!(old_pte & _PAGE_HASHPTE))) {
-               unsigned long hash = hpt_hash(va, shift, ssize);
-               unsigned long hpte_group;
-
-               pa = pte_pfn(__pte(old_pte)) << PAGE_SHIFT;
-
-repeat:
-               hpte_group = ((hash & htab_hash_mask) *
-                             HPTES_PER_GROUP) & ~0x7UL;
-
-               /* clear HPTE slot informations in new PTE */
-#ifdef CONFIG_PPC_64K_PAGES
-               new_pte = (new_pte & ~_PAGE_HPTEFLAGS) | _PAGE_HPTE_SUB0;
-#else
-               new_pte = (new_pte & ~_PAGE_HPTEFLAGS) | _PAGE_HASHPTE;
-#endif
-               /* Add in WIMG bits */
-               rflags |= (new_pte & (_PAGE_WRITETHRU | _PAGE_NO_CACHE |
-                                     _PAGE_COHERENT | _PAGE_GUARDED));
-
-               /* Insert into the hash table, primary slot */
-               slot = ppc_md.hpte_insert(hpte_group, va, pa, rflags, 0,
-                                         mmu_psize, ssize);
-
-               /* Primary is full, try the secondary */
-               if (unlikely(slot == -1)) {
-                       hpte_group = ((~hash & htab_hash_mask) *
-                                     HPTES_PER_GROUP) & ~0x7UL; 
-                       slot = ppc_md.hpte_insert(hpte_group, va, pa, rflags,
-                                                 HPTE_V_SECONDARY,
-                                                 mmu_psize, ssize);
-                       if (slot == -1) {
-                               if (mftb() & 0x1)
-                                       hpte_group = ((hash & htab_hash_mask) *
-                                                     HPTES_PER_GROUP)&~0x7UL;
-
-                               ppc_md.hpte_remove(hpte_group);
-                               goto repeat;
-                        }
-               }
+       int shift = __ffs(size);
+       int mmu_psize;
 
-               if (unlikely(slot == -2))
-                       panic("hash_huge_page: pte_insert failed\n");
+       /* Check that it is a page size supported by the hardware and
+        * that it fits within pagetable and slice limits. */
+       if (!is_power_of_2(size)
+           || (shift > SLICE_HIGH_SHIFT) || (shift <= PAGE_SHIFT))
+               return -EINVAL;
 
-               new_pte |= (slot << 12) & (_PAGE_F_SECOND | _PAGE_F_GIX);
-       }
+       if ((mmu_psize = shift_to_mmu_psize(shift)) < 0)
+               return -EINVAL;
 
-       /*
-        * No need to use ldarx/stdcx here
+#ifdef CONFIG_SPU_FS_64K_LS
+       /* Disable support for 64K huge pages when 64K SPU local store
+        * support is enabled as the current implementation conflicts.
         */
-       *ptep = __pte(new_pte & ~_PAGE_BUSY);
+       if (shift == PAGE_SHIFT_64K)
+               return -EINVAL;
+#endif /* CONFIG_SPU_FS_64K_LS */
 
-       err = 0;
+       BUG_ON(mmu_psize_defs[mmu_psize].shift != shift);
 
- out:
-       return err;
-}
-
-static void __init set_huge_psize(int psize)
-{
-       unsigned pdshift;
+       /* Return if huge page size has already been setup */
+       if (size_to_hstate(size))
+               return 0;
 
-       /* Check that it is a page size supported by the hardware and
-        * that it fits within pagetable limits. */
-       if (mmu_psize_defs[psize].shift &&
-               mmu_psize_defs[psize].shift < SID_SHIFT_1T &&
-               (mmu_psize_defs[psize].shift > MIN_HUGEPTE_SHIFT ||
-                mmu_psize_defs[psize].shift == PAGE_SHIFT_64K ||
-                mmu_psize_defs[psize].shift == PAGE_SHIFT_16G)) {
-               /* Return if huge page size has already been setup or is the
-                * same as the base page size. */
-               if (mmu_huge_psizes[psize] ||
-                  mmu_psize_defs[psize].shift == PAGE_SHIFT)
-                       return;
-               hugetlb_add_hstate(mmu_psize_defs[psize].shift - PAGE_SHIFT);
+       hugetlb_add_hstate(shift - PAGE_SHIFT);
 
-               if (mmu_psize_defs[psize].shift < PMD_SHIFT)
-                       pdshift = PMD_SHIFT;
-               else if (mmu_psize_defs[psize].shift < PUD_SHIFT)
-                       pdshift = PUD_SHIFT;
-               else
-                       pdshift = PGDIR_SHIFT;
-               mmu_huge_psizes[psize] = pdshift - mmu_psize_defs[psize].shift;
-       }
+       return 0;
 }
 
 static int __init hugepage_setup_sz(char *str)
 {
        unsigned long long size;
-       int mmu_psize;
-       int shift;
 
        size = memparse(str, &str);
 
-       shift = __ffs(size);
-       mmu_psize = shift_to_mmu_psize(shift);
-       if (mmu_psize >= 0 && mmu_psize_defs[mmu_psize].shift)
-               set_huge_psize(mmu_psize);
-       else
+       if (add_huge_page_size(size) != 0)
                printk(KERN_WARNING "Invalid huge page size specified(%llu)\n", size);
 
        return 1;
@@ -720,31 +532,50 @@ static int __init hugetlbpage_init(void)
        if (!cpu_has_feature(CPU_FTR_16M_PAGE))
                return -ENODEV;
 
-       /* Add supported huge page sizes.  Need to change
-        *  HUGE_MAX_HSTATE if the number of supported huge page sizes
-        *  changes.
-        */
-       set_huge_psize(MMU_PAGE_16M);
-       set_huge_psize(MMU_PAGE_16G);
+       for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) {
+               unsigned shift;
+               unsigned pdshift;
 
-       /* Temporarily disable support for 64K huge pages when 64K SPU local
-        * store support is enabled as the current implementation conflicts.
-        */
-#ifndef CONFIG_SPU_FS_64K_LS
-       set_huge_psize(MMU_PAGE_64K);
-#endif
+               if (!mmu_psize_defs[psize].shift)
+                       continue;
 
-       for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) {
-               if (mmu_huge_psizes[psize]) {
-                       pgtable_cache_add(mmu_huge_psizes[psize], NULL);
-                       if (!PGT_CACHE(mmu_huge_psizes[psize]))
-                               panic("hugetlbpage_init(): could not create "
-                                     "pgtable cache for %d bit pagesize\n",
-                                     mmu_psize_to_shift(psize));
-               }
+               shift = mmu_psize_to_shift(psize);
+
+               if (add_huge_page_size(1ULL << shift) < 0)
+                       continue;
+
+               if (shift < PMD_SHIFT)
+                       pdshift = PMD_SHIFT;
+               else if (shift < PUD_SHIFT)
+                       pdshift = PUD_SHIFT;
+               else
+                       pdshift = PGDIR_SHIFT;
+
+               pgtable_cache_add(pdshift - shift, NULL);
+               if (!PGT_CACHE(pdshift - shift))
+                       panic("hugetlbpage_init(): could not create "
+                             "pgtable cache for %d bit pagesize\n", shift);
        }
 
+       /* Set default large page size. Currently, we pick 16M or 1M
+        * depending on what is available
+        */
+       if (mmu_psize_defs[MMU_PAGE_16M].shift)
+               HPAGE_SHIFT = mmu_psize_defs[MMU_PAGE_16M].shift;
+       else if (mmu_psize_defs[MMU_PAGE_1M].shift)
+               HPAGE_SHIFT = mmu_psize_defs[MMU_PAGE_1M].shift;
+
        return 0;
 }
 
 module_init(hugetlbpage_init);
+
+void flush_dcache_icache_hugepage(struct page *page)
+{
+       int i;
+
+       BUG_ON(!PageCompound(page));
+
+       for (i = 0; i < (1UL << compound_order(page)); i++)
+               __flush_dcache_icache(page_address(page+i));
+}