Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[pandora-kernel.git] / arch / powerpc / mm / hugetlbpage.c
index fb95926..71efb38 100644 (file)
 #include <asm/mmu_context.h>
 #include <asm/machdep.h>
 #include <asm/cputable.h>
-#include <asm/tlb.h>
 #include <asm/spu.h>
 
-#include <linux/sysctl.h>
-
 #define NUM_LOW_AREAS  (0x100000000UL >> SID_SHIFT)
 #define NUM_HIGH_AREAS (PGTABLE_RANGE >> HTLB_AREA_SHIFT)
 
@@ -91,7 +88,7 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
        pgd_t *pg;
        pud_t *pu;
 
-       BUG_ON(! in_hugepage_area(mm->context, addr));
+       BUG_ON(get_slice_psize(mm, addr) != mmu_huge_psize);
 
        addr &= HPAGE_MASK;
 
@@ -119,7 +116,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr)
        pud_t *pu;
        hugepd_t *hpdp = NULL;
 
-       BUG_ON(! in_hugepage_area(mm->context, addr));
+       BUG_ON(get_slice_psize(mm, addr) != mmu_huge_psize);
 
        addr &= HPAGE_MASK;
 
@@ -302,7 +299,7 @@ void hugetlb_free_pgd_range(struct mmu_gather **tlb,
        start = addr;
        pgd = pgd_offset((*tlb)->mm, addr);
        do {
-               BUG_ON(! in_hugepage_area((*tlb)->mm->context, addr));
+               BUG_ON(get_slice_psize((*tlb)->mm, addr) != mmu_huge_psize);
                next = pgd_addr_end(addr, end);
                if (pgd_none_or_clear_bad(pgd))
                        continue;
@@ -331,203 +328,13 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
        return __pte(old);
 }
 
-struct slb_flush_info {
-       struct mm_struct *mm;
-       u16 newareas;
-};
-
-static void flush_low_segments(void *parm)
-{
-       struct slb_flush_info *fi = parm;
-       unsigned long i;
-
-       BUILD_BUG_ON((sizeof(fi->newareas)*8) != NUM_LOW_AREAS);
-
-       if (current->active_mm != fi->mm)
-               return;
-
-       /* Only need to do anything if this CPU is working in the same
-        * mm as the one which has changed */
-
-       /* update the paca copy of the context struct */
-       get_paca()->context = current->active_mm->context;
-
-       asm volatile("isync" : : : "memory");
-       for (i = 0; i < NUM_LOW_AREAS; i++) {
-               if (! (fi->newareas & (1U << i)))
-                       continue;
-               asm volatile("slbie %0"
-                            : : "r" ((i << SID_SHIFT) | SLBIE_C));
-       }
-       asm volatile("isync" : : : "memory");
-}
-
-static void flush_high_segments(void *parm)
-{
-       struct slb_flush_info *fi = parm;
-       unsigned long i, j;
-
-
-       BUILD_BUG_ON((sizeof(fi->newareas)*8) != NUM_HIGH_AREAS);
-
-       if (current->active_mm != fi->mm)
-               return;
-
-       /* Only need to do anything if this CPU is working in the same
-        * mm as the one which has changed */
-
-       /* update the paca copy of the context struct */
-       get_paca()->context = current->active_mm->context;
-
-       asm volatile("isync" : : : "memory");
-       for (i = 0; i < NUM_HIGH_AREAS; i++) {
-               if (! (fi->newareas & (1U << i)))
-                       continue;
-               for (j = 0; j < (1UL << (HTLB_AREA_SHIFT-SID_SHIFT)); j++)
-                       asm volatile("slbie %0"
-                                    :: "r" (((i << HTLB_AREA_SHIFT)
-                                             + (j << SID_SHIFT)) | SLBIE_C));
-       }
-       asm volatile("isync" : : : "memory");
-}
-
-static int prepare_low_area_for_htlb(struct mm_struct *mm, unsigned long area)
-{
-       unsigned long start = area << SID_SHIFT;
-       unsigned long end = (area+1) << SID_SHIFT;
-       struct vm_area_struct *vma;
-
-       BUG_ON(area >= NUM_LOW_AREAS);
-
-       /* Check no VMAs are in the region */
-       vma = find_vma(mm, start);
-       if (vma && (vma->vm_start < end))
-               return -EBUSY;
-
-       return 0;
-}
-
-static int prepare_high_area_for_htlb(struct mm_struct *mm, unsigned long area)
-{
-       unsigned long start = area << HTLB_AREA_SHIFT;
-       unsigned long end = (area+1) << HTLB_AREA_SHIFT;
-       struct vm_area_struct *vma;
-
-       BUG_ON(area >= NUM_HIGH_AREAS);
-
-       /* Hack, so that each addresses is controlled by exactly one
-        * of the high or low area bitmaps, the first high area starts
-        * at 4GB, not 0 */
-       if (start == 0)
-               start = 0x100000000UL;
-
-       /* Check no VMAs are in the region */
-       vma = find_vma(mm, start);
-       if (vma && (vma->vm_start < end))
-               return -EBUSY;
-
-       return 0;
-}
-
-static int open_low_hpage_areas(struct mm_struct *mm, u16 newareas)
-{
-       unsigned long i;
-       struct slb_flush_info fi;
-
-       BUILD_BUG_ON((sizeof(newareas)*8) != NUM_LOW_AREAS);
-       BUILD_BUG_ON((sizeof(mm->context.low_htlb_areas)*8) != NUM_LOW_AREAS);
-
-       newareas &= ~(mm->context.low_htlb_areas);
-       if (! newareas)
-               return 0; /* The segments we want are already open */
-
-       for (i = 0; i < NUM_LOW_AREAS; i++)
-               if ((1 << i) & newareas)
-                       if (prepare_low_area_for_htlb(mm, i) != 0)
-                               return -EBUSY;
-
-       mm->context.low_htlb_areas |= newareas;
-
-       /* the context change must make it to memory before the flush,
-        * so that further SLB misses do the right thing. */
-       mb();
-
-       fi.mm = mm;
-       fi.newareas = newareas;
-       on_each_cpu(flush_low_segments, &fi, 0, 1);
-
-       return 0;
-}
-
-static int open_high_hpage_areas(struct mm_struct *mm, u16 newareas)
-{
-       struct slb_flush_info fi;
-       unsigned long i;
-
-       BUILD_BUG_ON((sizeof(newareas)*8) != NUM_HIGH_AREAS);
-       BUILD_BUG_ON((sizeof(mm->context.high_htlb_areas)*8)
-                    != NUM_HIGH_AREAS);
-
-       newareas &= ~(mm->context.high_htlb_areas);
-       if (! newareas)
-               return 0; /* The areas we want are already open */
-
-       for (i = 0; i < NUM_HIGH_AREAS; i++)
-               if ((1 << i) & newareas)
-                       if (prepare_high_area_for_htlb(mm, i) != 0)
-                               return -EBUSY;
-
-       mm->context.high_htlb_areas |= newareas;
-
-       /* the context change must make it to memory before the flush,
-        * so that further SLB misses do the right thing. */
-       mb();
-
-       fi.mm = mm;
-       fi.newareas = newareas;
-       on_each_cpu(flush_high_segments, &fi, 0, 1);
-
-       return 0;
-}
-
-int prepare_hugepage_range(unsigned long addr, unsigned long len, pgoff_t pgoff)
-{
-       int err = 0;
-
-       if (pgoff & (~HPAGE_MASK >> PAGE_SHIFT))
-               return -EINVAL;
-       if (len & ~HPAGE_MASK)
-               return -EINVAL;
-       if (addr & ~HPAGE_MASK)
-               return -EINVAL;
-
-       if (addr < 0x100000000UL)
-               err = open_low_hpage_areas(current->mm,
-                                         LOW_ESID_MASK(addr, len));
-       if ((addr + len) > 0x100000000UL)
-               err = open_high_hpage_areas(current->mm,
-                                           HTLB_AREA_MASK(addr, len));
-#ifdef CONFIG_SPE_BASE
-       spu_flush_all_slbs(current->mm);
-#endif
-       if (err) {
-               printk(KERN_DEBUG "prepare_hugepage_range(%lx, %lx)"
-                      " failed (lowmask: 0x%04hx, highmask: 0x%04hx)\n",
-                      addr, len,
-                      LOW_ESID_MASK(addr, len), HTLB_AREA_MASK(addr, len));
-               return err;
-       }
-
-       return 0;
-}
-
 struct page *
 follow_huge_addr(struct mm_struct *mm, unsigned long address, int write)
 {
        pte_t *ptep;
        struct page *page;
 
-       if (! in_hugepage_area(mm->context, address))
+       if (get_slice_psize(mm, address) != mmu_huge_psize)
                return ERR_PTR(-EINVAL);
 
        ptep = huge_pte_offset(mm, address);
@@ -551,359 +358,13 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address,
        return NULL;
 }
 
-/* Because we have an exclusive hugepage region which lies within the
- * normal user address space, we have to take special measures to make
- * non-huge mmap()s evade the hugepage reserved regions. */
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
-                                    unsigned long len, unsigned long pgoff,
-                                    unsigned long flags)
-{
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma;
-       unsigned long start_addr;
-
-       if (len > TASK_SIZE)
-               return -ENOMEM;
-
-       /* handle fixed mapping: prevent overlap with huge pages */
-       if (flags & MAP_FIXED) {
-               if (is_hugepage_only_range(mm, addr, len))
-                       return -EINVAL;
-               return addr;
-       }
-
-       if (addr) {
-               addr = PAGE_ALIGN(addr);
-               vma = find_vma(mm, addr);
-               if (((TASK_SIZE - len) >= addr)
-                   && (!vma || (addr+len) <= vma->vm_start)
-                   && !is_hugepage_only_range(mm, addr,len))
-                       return addr;
-       }
-       if (len > mm->cached_hole_size) {
-               start_addr = addr = mm->free_area_cache;
-       } else {
-               start_addr = addr = TASK_UNMAPPED_BASE;
-               mm->cached_hole_size = 0;
-       }
-
-full_search:
-       vma = find_vma(mm, addr);
-       while (TASK_SIZE - len >= addr) {
-               BUG_ON(vma && (addr >= vma->vm_end));
-
-               if (touches_hugepage_low_range(mm, addr, len)) {
-                       addr = ALIGN(addr+1, 1<<SID_SHIFT);
-                       vma = find_vma(mm, addr);
-                       continue;
-               }
-               if (touches_hugepage_high_range(mm, addr, len)) {
-                       addr = ALIGN(addr+1, 1UL<<HTLB_AREA_SHIFT);
-                       vma = find_vma(mm, addr);
-                       continue;
-               }
-               if (!vma || addr + len <= vma->vm_start) {
-                       /*
-                        * Remember the place where we stopped the search:
-                        */
-                       mm->free_area_cache = addr + len;
-                       return addr;
-               }
-               if (addr + mm->cached_hole_size < vma->vm_start)
-                       mm->cached_hole_size = vma->vm_start - addr;
-               addr = vma->vm_end;
-               vma = vma->vm_next;
-       }
-
-       /* Make sure we didn't miss any holes */
-       if (start_addr != TASK_UNMAPPED_BASE) {
-               start_addr = addr = TASK_UNMAPPED_BASE;
-               mm->cached_hole_size = 0;
-               goto full_search;
-       }
-       return -ENOMEM;
-}
-
-/*
- * This mmap-allocator allocates new areas top-down from below the
- * stack's low limit (the base):
- *
- * Because we have an exclusive hugepage region which lies within the
- * normal user address space, we have to take special measures to make
- * non-huge mmap()s evade the hugepage reserved regions.
- */
-unsigned long
-arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
-                         const unsigned long len, const unsigned long pgoff,
-                         const unsigned long flags)
-{
-       struct vm_area_struct *vma, *prev_vma;
-       struct mm_struct *mm = current->mm;
-       unsigned long base = mm->mmap_base, addr = addr0;
-       unsigned long largest_hole = mm->cached_hole_size;
-       int first_time = 1;
-
-       /* requested length too big for entire address space */
-       if (len > TASK_SIZE)
-               return -ENOMEM;
-
-       /* handle fixed mapping: prevent overlap with huge pages */
-       if (flags & MAP_FIXED) {
-               if (is_hugepage_only_range(mm, addr, len))
-                       return -EINVAL;
-               return addr;
-       }
-
-       /* dont allow allocations above current base */
-       if (mm->free_area_cache > base)
-               mm->free_area_cache = base;
-
-       /* requesting a specific address */
-       if (addr) {
-               addr = PAGE_ALIGN(addr);
-               vma = find_vma(mm, addr);
-               if (TASK_SIZE - len >= addr &&
-                               (!vma || addr + len <= vma->vm_start)
-                               && !is_hugepage_only_range(mm, addr,len))
-                       return addr;
-       }
-
-       if (len <= largest_hole) {
-               largest_hole = 0;
-               mm->free_area_cache = base;
-       }
-try_again:
-       /* make sure it can fit in the remaining address space */
-       if (mm->free_area_cache < len)
-               goto fail;
-
-       /* either no address requested or cant fit in requested address hole */
-       addr = (mm->free_area_cache - len) & PAGE_MASK;
-       do {
-hugepage_recheck:
-               if (touches_hugepage_low_range(mm, addr, len)) {
-                       addr = (addr & ((~0) << SID_SHIFT)) - len;
-                       goto hugepage_recheck;
-               } else if (touches_hugepage_high_range(mm, addr, len)) {
-                       addr = (addr & ((~0UL) << HTLB_AREA_SHIFT)) - len;
-                       goto hugepage_recheck;
-               }
-
-               /*
-                * Lookup failure means no vma is above this address,
-                * i.e. return with success:
-                */
-               if (!(vma = find_vma_prev(mm, addr, &prev_vma)))
-                       return addr;
-
-               /*
-                * new region fits between prev_vma->vm_end and
-                * vma->vm_start, use it:
-                */
-               if (addr+len <= vma->vm_start &&
-                         (!prev_vma || (addr >= prev_vma->vm_end))) {
-                       /* remember the address as a hint for next time */
-                       mm->cached_hole_size = largest_hole;
-                       return (mm->free_area_cache = addr);
-               } else {
-                       /* pull free_area_cache down to the first hole */
-                       if (mm->free_area_cache == vma->vm_end) {
-                               mm->free_area_cache = vma->vm_start;
-                               mm->cached_hole_size = largest_hole;
-                       }
-               }
-
-               /* remember the largest hole we saw so far */
-               if (addr + largest_hole < vma->vm_start)
-                       largest_hole = vma->vm_start - addr;
-
-               /* try just below the current vma->vm_start */
-               addr = vma->vm_start-len;
-       } while (len <= vma->vm_start);
-
-fail:
-       /*
-        * if hint left us with no space for the requested
-        * mapping then try again:
-        */
-       if (first_time) {
-               mm->free_area_cache = base;
-               largest_hole = 0;
-               first_time = 0;
-               goto try_again;
-       }
-       /*
-        * A failed mmap() very likely causes application failure,
-        * so fall back to the bottom-up function here. This scenario
-        * can happen with large stack limits and large mmap()
-        * allocations.
-        */
-       mm->free_area_cache = TASK_UNMAPPED_BASE;
-       mm->cached_hole_size = ~0UL;
-       addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
-       /*
-        * Restore the topdown base:
-        */
-       mm->free_area_cache = base;
-       mm->cached_hole_size = ~0UL;
-
-       return addr;
-}
-
-static int htlb_check_hinted_area(unsigned long addr, unsigned long len)
-{
-       struct vm_area_struct *vma;
-
-       vma = find_vma(current->mm, addr);
-       if (TASK_SIZE - len >= addr &&
-           (!vma || ((addr + len) <= vma->vm_start)))
-               return 0;
-
-       return -ENOMEM;
-}
-
-static unsigned long htlb_get_low_area(unsigned long len, u16 segmask)
-{
-       unsigned long addr = 0;
-       struct vm_area_struct *vma;
-
-       vma = find_vma(current->mm, addr);
-       while (addr + len <= 0x100000000UL) {
-               BUG_ON(vma && (addr >= vma->vm_end)); /* invariant */
-
-               if (! __within_hugepage_low_range(addr, len, segmask)) {
-                       addr = ALIGN(addr+1, 1<<SID_SHIFT);
-                       vma = find_vma(current->mm, addr);
-                       continue;
-               }
-
-               if (!vma || (addr + len) <= vma->vm_start)
-                       return addr;
-               addr = ALIGN(vma->vm_end, HPAGE_SIZE);
-               /* Depending on segmask this might not be a confirmed
-                * hugepage region, so the ALIGN could have skipped
-                * some VMAs */
-               vma = find_vma(current->mm, addr);
-       }
-
-       return -ENOMEM;
-}
-
-static unsigned long htlb_get_high_area(unsigned long len, u16 areamask)
-{
-       unsigned long addr = 0x100000000UL;
-       struct vm_area_struct *vma;
-
-       vma = find_vma(current->mm, addr);
-       while (addr + len <= TASK_SIZE_USER64) {
-               BUG_ON(vma && (addr >= vma->vm_end)); /* invariant */
-
-               if (! __within_hugepage_high_range(addr, len, areamask)) {
-                       addr = ALIGN(addr+1, 1UL<<HTLB_AREA_SHIFT);
-                       vma = find_vma(current->mm, addr);
-                       continue;
-               }
-
-               if (!vma || (addr + len) <= vma->vm_start)
-                       return addr;
-               addr = ALIGN(vma->vm_end, HPAGE_SIZE);
-               /* Depending on segmask this might not be a confirmed
-                * hugepage region, so the ALIGN could have skipped
-                * some VMAs */
-               vma = find_vma(current->mm, addr);
-       }
-
-       return -ENOMEM;
-}
 
 unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
                                        unsigned long len, unsigned long pgoff,
                                        unsigned long flags)
 {
-       int lastshift;
-       u16 areamask, curareas;
-
-       if (HPAGE_SHIFT == 0)
-               return -EINVAL;
-       if (len & ~HPAGE_MASK)
-               return -EINVAL;
-       if (len > TASK_SIZE)
-               return -ENOMEM;
-
-       if (!cpu_has_feature(CPU_FTR_16M_PAGE))
-               return -EINVAL;
-
-       /* Paranoia, caller should have dealt with this */
-       BUG_ON((addr + len)  < addr);
-
-       /* Handle MAP_FIXED */
-       if (flags & MAP_FIXED) {
-               if (prepare_hugepage_range(addr, len, pgoff))
-                       return -EINVAL;
-               return addr;
-       }
-
-       if (test_thread_flag(TIF_32BIT)) {
-               curareas = current->mm->context.low_htlb_areas;
-
-               /* First see if we can use the hint address */
-               if (addr && (htlb_check_hinted_area(addr, len) == 0)) {
-                       areamask = LOW_ESID_MASK(addr, len);
-                       if (open_low_hpage_areas(current->mm, areamask) == 0)
-                               return addr;
-               }
-
-               /* Next see if we can map in the existing low areas */
-               addr = htlb_get_low_area(len, curareas);
-               if (addr != -ENOMEM)
-                       return addr;
-
-               /* Finally go looking for areas to open */
-               lastshift = 0;
-               for (areamask = LOW_ESID_MASK(0x100000000UL-len, len);
-                    ! lastshift; areamask >>=1) {
-                       if (areamask & 1)
-                               lastshift = 1;
-
-                       addr = htlb_get_low_area(len, curareas | areamask);
-                       if ((addr != -ENOMEM)
-                           && open_low_hpage_areas(current->mm, areamask) == 0)
-                               return addr;
-               }
-       } else {
-               curareas = current->mm->context.high_htlb_areas;
-
-               /* First see if we can use the hint address */
-               /* We discourage 64-bit processes from doing hugepage
-                * mappings below 4GB (must use MAP_FIXED) */
-               if ((addr >= 0x100000000UL)
-                   && (htlb_check_hinted_area(addr, len) == 0)) {
-                       areamask = HTLB_AREA_MASK(addr, len);
-                       if (open_high_hpage_areas(current->mm, areamask) == 0)
-                               return addr;
-               }
-
-               /* Next see if we can map in the existing high areas */
-               addr = htlb_get_high_area(len, curareas);
-               if (addr != -ENOMEM)
-                       return addr;
-
-               /* Finally go looking for areas to open */
-               lastshift = 0;
-               for (areamask = HTLB_AREA_MASK(TASK_SIZE_USER64-len, len);
-                    ! lastshift; areamask >>=1) {
-                       if (areamask & 1)
-                               lastshift = 1;
-
-                       addr = htlb_get_high_area(len, curareas | areamask);
-                       if ((addr != -ENOMEM)
-                           && open_high_hpage_areas(current->mm, areamask) == 0)
-                               return addr;
-               }
-       }
-       printk(KERN_DEBUG "hugetlb_get_unmapped_area() unable to open"
-              " enough areas\n");
-       return -ENOMEM;
+       return slice_get_unmapped_area(addr, len, flags,
+                                      mmu_huge_psize, 1, 0);
 }
 
 /*
@@ -942,11 +403,12 @@ int hash_huge_page(struct mm_struct *mm, unsigned long access,
        unsigned long va, rflags, pa;
        long slot;
        int err = 1;
+       int ssize = user_segment_size(ea);
 
        ptep = huge_pte_offset(mm, ea);
 
        /* Search the Linux page table for a match with va */
-       va = (vsid << 28) | (ea & 0x0fffffff);
+       va = hpt_va(ea, vsid, ssize);
 
        /*
         * If no pte found or not present, send the problem up to
@@ -997,19 +459,19 @@ int hash_huge_page(struct mm_struct *mm, unsigned long access,
                /* There MIGHT be an HPTE for this pte */
                unsigned long hash, slot;
 
-               hash = hpt_hash(va, HPAGE_SHIFT);
+               hash = hpt_hash(va, HPAGE_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_huge_psize,
-                                        local) == -1)
+                                        ssize, local) == -1)
                        old_pte &= ~_PAGE_HPTEFLAGS;
        }
 
        if (likely(!(old_pte & _PAGE_HASHPTE))) {
-               unsigned long hash = hpt_hash(va, HPAGE_SHIFT);
+               unsigned long hash = hpt_hash(va, HPAGE_SHIFT, ssize);
                unsigned long hpte_group;
 
                pa = pte_pfn(__pte(old_pte)) << PAGE_SHIFT;
@@ -1028,7 +490,7 @@ repeat:
 
                /* Insert into the hash table, primary slot */
                slot = ppc_md.hpte_insert(hpte_group, va, pa, rflags, 0,
-                                         mmu_huge_psize);
+                                         mmu_huge_psize, ssize);
 
                /* Primary is full, try the secondary */
                if (unlikely(slot == -1)) {
@@ -1036,7 +498,7 @@ repeat:
                                      HPTES_PER_GROUP) & ~0x7UL; 
                        slot = ppc_md.hpte_insert(hpte_group, va, pa, rflags,
                                                  HPTE_V_SECONDARY,
-                                                 mmu_huge_psize);
+                                                 mmu_huge_psize, ssize);
                        if (slot == -1) {
                                if (mftb() & 0x1)
                                        hpte_group = ((hash & htab_hash_mask) *
@@ -1064,7 +526,7 @@ repeat:
        return err;
 }
 
-static void zero_ctor(void *addr, struct kmem_cache *cache, unsigned long flags)
+static void zero_ctor(struct kmem_cache *cache, void *addr)
 {
        memset(addr, 0, kmem_cache_size(cache));
 }
@@ -1078,7 +540,7 @@ static int __init hugetlbpage_init(void)
                                               HUGEPTE_TABLE_SIZE,
                                               HUGEPTE_TABLE_SIZE,
                                               0,
-                                              zero_ctor, NULL);
+                                              zero_ctor);
        if (! huge_pgtable_cache)
                panic("hugetlbpage_init(): could not create hugepte cache\n");