]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - arch/powerpc/mm/slice.c
Merge tag 'powerpc-4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
[linux.git] / arch / powerpc / mm / slice.c
index 23ec2c5e3b782412f8b10717cee352e56cc31217..9cd87d11fe4e70e353521c6ac14eddeb4bc93ce0 100644 (file)
 #include <asm/hugetlb.h>
 
 static DEFINE_SPINLOCK(slice_convert_lock);
-/*
- * One bit per slice. We have lower slices which cover 256MB segments
- * upto 4G range. That gets us 16 low slices. For the rest we track slices
- * in 1TB size.
- */
-struct slice_mask {
-       u64 low_slices;
-       DECLARE_BITMAP(high_slices, SLICE_NUM_HIGH);
-};
 
 #ifdef DEBUG
 int _slice_debug = 1;
 
-static void slice_print_mask(const char *label, struct slice_mask mask)
+static void slice_print_mask(const char *label, const struct slice_mask *mask)
 {
        if (!_slice_debug)
                return;
-       pr_devel("%s low_slice: %*pbl\n", label, (int)SLICE_NUM_LOW, &mask.low_slices);
-       pr_devel("%s high_slice: %*pbl\n", label, (int)SLICE_NUM_HIGH, mask.high_slices);
+       pr_devel("%s low_slice: %*pbl\n", label,
+                       (int)SLICE_NUM_LOW, &mask->low_slices);
+       pr_devel("%s high_slice: %*pbl\n", label,
+                       (int)SLICE_NUM_HIGH, mask->high_slices);
 }
 
 #define slice_dbg(fmt...) do { if (_slice_debug) pr_devel(fmt); } while (0)
 
 #else
 
-static void slice_print_mask(const char *label, struct slice_mask mask) {}
+static void slice_print_mask(const char *label, const struct slice_mask *mask) {}
 #define slice_dbg(fmt...)
 
 #endif
@@ -73,10 +66,12 @@ static void slice_range_to_mask(unsigned long start, unsigned long len,
        unsigned long end = start + len - 1;
 
        ret->low_slices = 0;
-       bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
+       if (SLICE_NUM_HIGH)
+               bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
 
        if (start < SLICE_LOW_TOP) {
-               unsigned long mend = min(end, (SLICE_LOW_TOP - 1));
+               unsigned long mend = min(end,
+                                        (unsigned long)(SLICE_LOW_TOP - 1));
 
                ret->low_slices = (1u << (GET_LOW_SLICE_INDEX(mend) + 1))
                        - (1u << GET_LOW_SLICE_INDEX(start));
@@ -113,11 +108,13 @@ static int slice_high_has_vma(struct mm_struct *mm, unsigned long slice)
        unsigned long start = slice << SLICE_HIGH_SHIFT;
        unsigned long end = start + (1ul << SLICE_HIGH_SHIFT);
 
+#ifdef CONFIG_PPC64
        /* 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 = SLICE_LOW_TOP;
+#endif
 
        return !slice_area_is_free(mm, start, end - start);
 }
@@ -128,7 +125,8 @@ static void slice_mask_for_free(struct mm_struct *mm, struct slice_mask *ret,
        unsigned long i;
 
        ret->low_slices = 0;
-       bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
+       if (SLICE_NUM_HIGH)
+               bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
 
        for (i = 0; i < SLICE_NUM_LOW; i++)
                if (!slice_low_has_vma(mm, i))
@@ -142,53 +140,75 @@ static void slice_mask_for_free(struct mm_struct *mm, struct slice_mask *ret,
                        __set_bit(i, ret->high_slices);
 }
 
-static void slice_mask_for_size(struct mm_struct *mm, int psize, struct slice_mask *ret,
-                               unsigned long high_limit)
+#ifdef CONFIG_PPC_BOOK3S_64
+static struct slice_mask *slice_mask_for_size(struct mm_struct *mm, int psize)
 {
-       unsigned char *hpsizes;
-       int index, mask_index;
-       unsigned long i;
-       u64 lpsizes;
-
-       ret->low_slices = 0;
-       bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
+#ifdef CONFIG_PPC_64K_PAGES
+       if (psize == MMU_PAGE_64K)
+               return &mm->context.mask_64k;
+#endif
+       if (psize == MMU_PAGE_4K)
+               return &mm->context.mask_4k;
+#ifdef CONFIG_HUGETLB_PAGE
+       if (psize == MMU_PAGE_16M)
+               return &mm->context.mask_16m;
+       if (psize == MMU_PAGE_16G)
+               return &mm->context.mask_16g;
+#endif
+       BUG();
+}
+#elif defined(CONFIG_PPC_8xx)
+static struct slice_mask *slice_mask_for_size(struct mm_struct *mm, int psize)
+{
+       if (psize == mmu_virtual_psize)
+               return &mm->context.mask_base_psize;
+#ifdef CONFIG_HUGETLB_PAGE
+       if (psize == MMU_PAGE_512K)
+               return &mm->context.mask_512k;
+       if (psize == MMU_PAGE_8M)
+               return &mm->context.mask_8m;
+#endif
+       BUG();
+}
+#else
+#error "Must define the slice masks for page sizes supported by the platform"
+#endif
 
-       lpsizes = mm->context.low_slices_psize;
-       for (i = 0; i < SLICE_NUM_LOW; i++)
-               if (((lpsizes >> (i * 4)) & 0xf) == psize)
-                       ret->low_slices |= 1u << i;
+static bool slice_check_range_fits(struct mm_struct *mm,
+                          const struct slice_mask *available,
+                          unsigned long start, unsigned long len)
+{
+       unsigned long end = start + len - 1;
+       u64 low_slices = 0;
 
-       if (high_limit <= SLICE_LOW_TOP)
-               return;
+       if (start < SLICE_LOW_TOP) {
+               unsigned long mend = min(end,
+                                        (unsigned long)(SLICE_LOW_TOP - 1));
 
-       hpsizes = mm->context.high_slices_psize;
-       for (i = 0; i < GET_HIGH_SLICE_INDEX(high_limit); i++) {
-               mask_index = i & 0x1;
-               index = i >> 1;
-               if (((hpsizes[index] >> (mask_index * 4)) & 0xf) == psize)
-                       __set_bit(i, ret->high_slices);
+               low_slices = (1u << (GET_LOW_SLICE_INDEX(mend) + 1))
+                               - (1u << GET_LOW_SLICE_INDEX(start));
        }
-}
+       if ((low_slices & available->low_slices) != low_slices)
+               return false;
 
-static int slice_check_fit(struct mm_struct *mm,
-                          struct slice_mask mask, struct slice_mask available)
-{
-       DECLARE_BITMAP(result, SLICE_NUM_HIGH);
-       /*
-        * Make sure we just do bit compare only to the max
-        * addr limit and not the full bit map size.
-        */
-       unsigned long slice_count = GET_HIGH_SLICE_INDEX(mm->context.slb_addr_limit);
+       if (SLICE_NUM_HIGH && ((start + len) > SLICE_LOW_TOP)) {
+               unsigned long start_index = GET_HIGH_SLICE_INDEX(start);
+               unsigned long align_end = ALIGN(end, (1UL << SLICE_HIGH_SHIFT));
+               unsigned long count = GET_HIGH_SLICE_INDEX(align_end) - start_index;
+               unsigned long i;
 
-       bitmap_and(result, mask.high_slices,
-                  available.high_slices, slice_count);
+               for (i = start_index; i < start_index + count; i++) {
+                       if (!test_bit(i, available->high_slices))
+                               return false;
+               }
+       }
 
-       return (mask.low_slices & available.low_slices) == mask.low_slices &&
-               bitmap_equal(result, mask.high_slices, slice_count);
+       return true;
 }
 
 static void slice_flush_segments(void *parm)
 {
+#ifdef CONFIG_PPC64
        struct mm_struct *mm = parm;
        unsigned long flags;
 
@@ -200,40 +220,64 @@ static void slice_flush_segments(void *parm)
        local_irq_save(flags);
        slb_flush_and_rebolt();
        local_irq_restore(flags);
+#endif
 }
 
-static void slice_convert(struct mm_struct *mm, struct slice_mask mask, int psize)
+static void slice_convert(struct mm_struct *mm,
+                               const struct slice_mask *mask, int psize)
 {
        int index, mask_index;
        /* Write the new slice psize bits */
-       unsigned char *hpsizes;
-       u64 lpsizes;
+       unsigned char *hpsizes, *lpsizes;
+       struct slice_mask *psize_mask, *old_mask;
        unsigned long i, flags;
+       int old_psize;
 
        slice_dbg("slice_convert(mm=%p, psize=%d)\n", mm, psize);
        slice_print_mask(" mask", mask);
 
+       psize_mask = slice_mask_for_size(mm, psize);
+
        /* We need to use a spinlock here to protect against
         * concurrent 64k -> 4k demotion ...
         */
        spin_lock_irqsave(&slice_convert_lock, flags);
 
        lpsizes = mm->context.low_slices_psize;
-       for (i = 0; i < SLICE_NUM_LOW; i++)
-               if (mask.low_slices & (1u << i))
-                       lpsizes = (lpsizes & ~(0xful << (i * 4))) |
-                               (((unsigned long)psize) << (i * 4));
+       for (i = 0; i < SLICE_NUM_LOW; i++) {
+               if (!(mask->low_slices & (1u << i)))
+                       continue;
+
+               mask_index = i & 0x1;
+               index = i >> 1;
 
-       /* Assign the value back */
-       mm->context.low_slices_psize = lpsizes;
+               /* Update the slice_mask */
+               old_psize = (lpsizes[index] >> (mask_index * 4)) & 0xf;
+               old_mask = slice_mask_for_size(mm, old_psize);
+               old_mask->low_slices &= ~(1u << i);
+               psize_mask->low_slices |= 1u << i;
+
+               /* Update the sizes array */
+               lpsizes[index] = (lpsizes[index] & ~(0xf << (mask_index * 4))) |
+                               (((unsigned long)psize) << (mask_index * 4));
+       }
 
        hpsizes = mm->context.high_slices_psize;
        for (i = 0; i < GET_HIGH_SLICE_INDEX(mm->context.slb_addr_limit); i++) {
+               if (!test_bit(i, mask->high_slices))
+                       continue;
+
                mask_index = i & 0x1;
                index = i >> 1;
-               if (test_bit(i, mask.high_slices))
-                       hpsizes[index] = (hpsizes[index] &
-                                         ~(0xf << (mask_index * 4))) |
+
+               /* Update the slice_mask */
+               old_psize = (hpsizes[index] >> (mask_index * 4)) & 0xf;
+               old_mask = slice_mask_for_size(mm, old_psize);
+               __clear_bit(i, old_mask->high_slices);
+               __set_bit(i, psize_mask->high_slices);
+
+               /* Update the sizes array */
+               hpsizes[index] = (hpsizes[index] & ~(0xf << (mask_index * 4))) |
                                (((unsigned long)psize) << (mask_index * 4));
        }
 
@@ -254,26 +298,25 @@ static void slice_convert(struct mm_struct *mm, struct slice_mask mask, int psiz
  * 'available' slice_mark.
  */
 static bool slice_scan_available(unsigned long addr,
-                                struct slice_mask available,
-                                int end,
-                                unsigned long *boundary_addr)
+                                const struct slice_mask *available,
+                                int end, unsigned long *boundary_addr)
 {
        unsigned long slice;
        if (addr < SLICE_LOW_TOP) {
                slice = GET_LOW_SLICE_INDEX(addr);
                *boundary_addr = (slice + end) << SLICE_LOW_SHIFT;
-               return !!(available.low_slices & (1u << slice));
+               return !!(available->low_slices & (1u << slice));
        } else {
                slice = GET_HIGH_SLICE_INDEX(addr);
                *boundary_addr = (slice + end) ?
                        ((slice + end) << SLICE_HIGH_SHIFT) : SLICE_LOW_TOP;
-               return !!test_bit(slice, available.high_slices);
+               return !!test_bit(slice, available->high_slices);
        }
 }
 
 static unsigned long slice_find_area_bottomup(struct mm_struct *mm,
                                              unsigned long len,
-                                             struct slice_mask available,
+                                             const struct slice_mask *available,
                                              int psize, unsigned long high_limit)
 {
        int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
@@ -319,7 +362,7 @@ static unsigned long slice_find_area_bottomup(struct mm_struct *mm,
 
 static unsigned long slice_find_area_topdown(struct mm_struct *mm,
                                             unsigned long len,
-                                            struct slice_mask available,
+                                            const struct slice_mask *available,
                                             int psize, unsigned long high_limit)
 {
        int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
@@ -377,7 +420,7 @@ static unsigned long slice_find_area_topdown(struct mm_struct *mm,
 
 
 static unsigned long slice_find_area(struct mm_struct *mm, unsigned long len,
-                                    struct slice_mask mask, int psize,
+                                    const struct slice_mask *mask, int psize,
                                     int topdown, unsigned long high_limit)
 {
        if (topdown)
@@ -386,23 +429,33 @@ static unsigned long slice_find_area(struct mm_struct *mm, unsigned long len,
                return slice_find_area_bottomup(mm, len, mask, psize, high_limit);
 }
 
-static inline void slice_or_mask(struct slice_mask *dst, struct slice_mask *src)
+static inline void slice_copy_mask(struct slice_mask *dst,
+                                       const struct slice_mask *src)
 {
-       DECLARE_BITMAP(result, SLICE_NUM_HIGH);
-
-       dst->low_slices |= src->low_slices;
-       bitmap_or(result, dst->high_slices, src->high_slices, SLICE_NUM_HIGH);
-       bitmap_copy(dst->high_slices, result, SLICE_NUM_HIGH);
+       dst->low_slices = src->low_slices;
+       if (!SLICE_NUM_HIGH)
+               return;
+       bitmap_copy(dst->high_slices, src->high_slices, SLICE_NUM_HIGH);
 }
 
-static inline void slice_andnot_mask(struct slice_mask *dst, struct slice_mask *src)
+static inline void slice_or_mask(struct slice_mask *dst,
+                                       const struct slice_mask *src1,
+                                       const struct slice_mask *src2)
 {
-       DECLARE_BITMAP(result, SLICE_NUM_HIGH);
-
-       dst->low_slices &= ~src->low_slices;
+       dst->low_slices = src1->low_slices | src2->low_slices;
+       if (!SLICE_NUM_HIGH)
+               return;
+       bitmap_or(dst->high_slices, src1->high_slices, src2->high_slices, SLICE_NUM_HIGH);
+}
 
-       bitmap_andnot(result, dst->high_slices, src->high_slices, SLICE_NUM_HIGH);
-       bitmap_copy(dst->high_slices, result, SLICE_NUM_HIGH);
+static inline void slice_andnot_mask(struct slice_mask *dst,
+                                       const struct slice_mask *src1,
+                                       const struct slice_mask *src2)
+{
+       dst->low_slices = src1->low_slices & ~src2->low_slices;
+       if (!SLICE_NUM_HIGH)
+               return;
+       bitmap_andnot(dst->high_slices, src1->high_slices, src2->high_slices, SLICE_NUM_HIGH);
 }
 
 #ifdef CONFIG_PPC_64K_PAGES
@@ -415,10 +468,10 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
                                      unsigned long flags, unsigned int psize,
                                      int topdown)
 {
-       struct slice_mask mask;
        struct slice_mask good_mask;
        struct slice_mask potential_mask;
-       struct slice_mask compat_mask;
+       const struct slice_mask *maskp;
+       const struct slice_mask *compat_maskp = NULL;
        int fixed = (flags & MAP_FIXED);
        int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
        unsigned long page_size = 1UL << pshift;
@@ -442,23 +495,16 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
        }
 
        if (high_limit > mm->context.slb_addr_limit) {
+               /*
+                * Increasing the slb_addr_limit does not require
+                * slice mask cache to be recalculated because it should
+                * be already initialised beyond the old address limit.
+                */
                mm->context.slb_addr_limit = high_limit;
+
                on_each_cpu(slice_flush_segments, mm, 1);
        }
 
-       /*
-        * init different masks
-        */
-       mask.low_slices = 0;
-       bitmap_zero(mask.high_slices, SLICE_NUM_HIGH);
-
-       /* silence stupid warning */;
-       potential_mask.low_slices = 0;
-       bitmap_zero(potential_mask.high_slices, SLICE_NUM_HIGH);
-
-       compat_mask.low_slices = 0;
-       bitmap_zero(compat_mask.high_slices, SLICE_NUM_HIGH);
-
        /* Sanity checks */
        BUG_ON(mm->task_size == 0);
        BUG_ON(mm->context.slb_addr_limit == 0);
@@ -481,8 +527,7 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
        /* First make up a "good" mask of slices that have the right size
         * already
         */
-       slice_mask_for_size(mm, psize, &good_mask, high_limit);
-       slice_print_mask(" good_mask", good_mask);
+       maskp = slice_mask_for_size(mm, psize);
 
        /*
         * Here "good" means slices that are already the right page size,
@@ -503,40 +548,47 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
         *      search in good | compat | free, found => convert free.
         */
 
-#ifdef CONFIG_PPC_64K_PAGES
-       /* If we support combo pages, we can allow 64k pages in 4k slices */
-       if (psize == MMU_PAGE_64K) {
-               slice_mask_for_size(mm, MMU_PAGE_4K, &compat_mask, high_limit);
+       /*
+        * If we support combo pages, we can allow 64k pages in 4k slices
+        * The mask copies could be avoided in most cases here if we had
+        * a pointer to good mask for the next code to use.
+        */
+       if (IS_ENABLED(CONFIG_PPC_64K_PAGES) && psize == MMU_PAGE_64K) {
+               compat_maskp = slice_mask_for_size(mm, MMU_PAGE_4K);
                if (fixed)
-                       slice_or_mask(&good_mask, &compat_mask);
+                       slice_or_mask(&good_mask, maskp, compat_maskp);
+               else
+                       slice_copy_mask(&good_mask, maskp);
+       } else {
+               slice_copy_mask(&good_mask, maskp);
        }
-#endif
+
+       slice_print_mask(" good_mask", &good_mask);
+       if (compat_maskp)
+               slice_print_mask(" compat_mask", compat_maskp);
 
        /* First check hint if it's valid or if we have MAP_FIXED */
        if (addr != 0 || fixed) {
-               /* Build a mask for the requested range */
-               slice_range_to_mask(addr, len, &mask);
-               slice_print_mask(" mask", mask);
-
                /* Check if we fit in the good mask. If we do, we just return,
                 * nothing else to do
                 */
-               if (slice_check_fit(mm, mask, good_mask)) {
+               if (slice_check_range_fits(mm, &good_mask, addr, len)) {
                        slice_dbg(" fits good !\n");
-                       return addr;
+                       newaddr = addr;
+                       goto return_addr;
                }
        } else {
                /* Now let's see if we can find something in the existing
                 * slices for that size
                 */
-               newaddr = slice_find_area(mm, len, good_mask,
+               newaddr = slice_find_area(mm, len, &good_mask,
                                          psize, topdown, high_limit);
                if (newaddr != -ENOMEM) {
                        /* Found within the good mask, we don't have to setup,
                         * we thus return directly
                         */
                        slice_dbg(" found area at 0x%lx\n", newaddr);
-                       return newaddr;
+                       goto return_addr;
                }
        }
        /*
@@ -544,12 +596,15 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
         * empty and thus can be converted
         */
        slice_mask_for_free(mm, &potential_mask, high_limit);
-       slice_or_mask(&potential_mask, &good_mask);
-       slice_print_mask(" potential", potential_mask);
+       slice_or_mask(&potential_mask, &potential_mask, &good_mask);
+       slice_print_mask(" potential", &potential_mask);
 
-       if ((addr != 0 || fixed) && slice_check_fit(mm, mask, potential_mask)) {
-               slice_dbg(" fits potential !\n");
-               goto convert;
+       if (addr != 0 || fixed) {
+               if (slice_check_range_fits(mm, &potential_mask, addr, len)) {
+                       slice_dbg(" fits potential !\n");
+                       newaddr = addr;
+                       goto convert;
+               }
        }
 
        /* If we have MAP_FIXED and failed the above steps, then error out */
@@ -562,46 +617,64 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
         * anywhere in the good area.
         */
        if (addr) {
-               addr = slice_find_area(mm, len, good_mask,
-                                      psize, topdown, high_limit);
-               if (addr != -ENOMEM) {
-                       slice_dbg(" found area at 0x%lx\n", addr);
-                       return addr;
+               newaddr = slice_find_area(mm, len, &good_mask,
+                                         psize, topdown, high_limit);
+               if (newaddr != -ENOMEM) {
+                       slice_dbg(" found area at 0x%lx\n", newaddr);
+                       goto return_addr;
                }
        }
 
        /* Now let's see if we can find something in the existing slices
         * for that size plus free slices
         */
-       addr = slice_find_area(mm, len, potential_mask,
-                              psize, topdown, high_limit);
+       newaddr = slice_find_area(mm, len, &potential_mask,
+                                 psize, topdown, high_limit);
 
 #ifdef CONFIG_PPC_64K_PAGES
-       if (addr == -ENOMEM && psize == MMU_PAGE_64K) {
+       if (newaddr == -ENOMEM && psize == MMU_PAGE_64K) {
                /* retry the search with 4k-page slices included */
-               slice_or_mask(&potential_mask, &compat_mask);
-               addr = slice_find_area(mm, len, potential_mask,
-                                      psize, topdown, high_limit);
+               slice_or_mask(&potential_mask, &potential_mask, compat_maskp);
+               newaddr = slice_find_area(mm, len, &potential_mask,
+                                         psize, topdown, high_limit);
        }
 #endif
 
-       if (addr == -ENOMEM)
+       if (newaddr == -ENOMEM)
                return -ENOMEM;
 
-       slice_range_to_mask(addr, len, &mask);
-       slice_dbg(" found potential area at 0x%lx\n", addr);
-       slice_print_mask(" mask", mask);
+       slice_range_to_mask(newaddr, len, &potential_mask);
+       slice_dbg(" found potential area at 0x%lx\n", newaddr);
+       slice_print_mask(" mask", &potential_mask);
 
  convert:
-       slice_andnot_mask(&mask, &good_mask);
-       slice_andnot_mask(&mask, &compat_mask);
-       if (mask.low_slices || !bitmap_empty(mask.high_slices, SLICE_NUM_HIGH)) {
-               slice_convert(mm, mask, psize);
+       /*
+        * Try to allocate the context before we do slice convert
+        * so that we handle the context allocation failure gracefully.
+        */
+       if (need_extra_context(mm, newaddr)) {
+               if (alloc_extended_context(mm, newaddr) < 0)
+                       return -ENOMEM;
+       }
+
+       slice_andnot_mask(&potential_mask, &potential_mask, &good_mask);
+       if (compat_maskp && !fixed)
+               slice_andnot_mask(&potential_mask, &potential_mask, compat_maskp);
+       if (potential_mask.low_slices ||
+               (SLICE_NUM_HIGH &&
+                !bitmap_empty(potential_mask.high_slices, SLICE_NUM_HIGH))) {
+               slice_convert(mm, &potential_mask, psize);
                if (psize > MMU_PAGE_BASE)
                        on_each_cpu(slice_flush_segments, mm, 1);
        }
-       return addr;
+       return newaddr;
 
+return_addr:
+       if (need_extra_context(mm, newaddr)) {
+               if (alloc_extended_context(mm, newaddr) < 0)
+                       return -ENOMEM;
+       }
+       return newaddr;
 }
 EXPORT_SYMBOL_GPL(slice_get_unmapped_area);
 
@@ -627,94 +700,60 @@ unsigned long arch_get_unmapped_area_topdown(struct file *filp,
 
 unsigned int get_slice_psize(struct mm_struct *mm, unsigned long addr)
 {
-       unsigned char *hpsizes;
+       unsigned char *psizes;
        int index, mask_index;
 
-       /*
-        * Radix doesn't use slice, but can get enabled along with MMU_SLICE
-        */
-       if (radix_enabled()) {
-#ifdef CONFIG_PPC_64K_PAGES
-               return MMU_PAGE_64K;
-#else
-               return MMU_PAGE_4K;
-#endif
-       }
+       VM_BUG_ON(radix_enabled());
+
        if (addr < SLICE_LOW_TOP) {
-               u64 lpsizes;
-               lpsizes = mm->context.low_slices_psize;
+               psizes = mm->context.low_slices_psize;
                index = GET_LOW_SLICE_INDEX(addr);
-               return (lpsizes >> (index * 4)) & 0xf;
+       } else {
+               psizes = mm->context.high_slices_psize;
+               index = GET_HIGH_SLICE_INDEX(addr);
        }
-       hpsizes = mm->context.high_slices_psize;
-       index = GET_HIGH_SLICE_INDEX(addr);
        mask_index = index & 0x1;
-       return (hpsizes[index >> 1] >> (mask_index * 4)) & 0xf;
+       return (psizes[index >> 1] >> (mask_index * 4)) & 0xf;
 }
 EXPORT_SYMBOL_GPL(get_slice_psize);
 
-/*
- * This is called by hash_page when it needs to do a lazy conversion of
- * an address space from real 64K pages to combo 4K pages (typically
- * when hitting a non cacheable mapping on a processor or hypervisor
- * that won't allow them for 64K pages).
- *
- * This is also called in init_new_context() to change back the user
- * psize from whatever the parent context had it set to
- * N.B. This may be called before mm->context.id has been set.
- *
- * This function will only change the content of the {low,high)_slice_psize
- * masks, it will not flush SLBs as this shall be handled lazily by the
- * caller.
- */
-void slice_set_user_psize(struct mm_struct *mm, unsigned int psize)
+void slice_init_new_context_exec(struct mm_struct *mm)
 {
-       int index, mask_index;
-       unsigned char *hpsizes;
-       unsigned long flags, lpsizes;
-       unsigned int old_psize;
-       int i;
+       unsigned char *hpsizes, *lpsizes;
+       struct slice_mask *mask;
+       unsigned int psize = mmu_virtual_psize;
 
-       slice_dbg("slice_set_user_psize(mm=%p, psize=%d)\n", mm, psize);
+       slice_dbg("slice_init_new_context_exec(mm=%p)\n", mm);
 
-       VM_BUG_ON(radix_enabled());
-       spin_lock_irqsave(&slice_convert_lock, flags);
-
-       old_psize = mm->context.user_psize;
-       slice_dbg(" old_psize=%d\n", old_psize);
-       if (old_psize == psize)
-               goto bail;
+       /*
+        * In the case of exec, use the default limit. In the
+        * case of fork it is just inherited from the mm being
+        * duplicated.
+        */
+#ifdef CONFIG_PPC64
+       mm->context.slb_addr_limit = DEFAULT_MAP_WINDOW_USER64;
+#else
+       mm->context.slb_addr_limit = DEFAULT_MAP_WINDOW;
+#endif
 
        mm->context.user_psize = psize;
-       wmb();
 
+       /*
+        * Set all slice psizes to the default.
+        */
        lpsizes = mm->context.low_slices_psize;
-       for (i = 0; i < SLICE_NUM_LOW; i++)
-               if (((lpsizes >> (i * 4)) & 0xf) == old_psize)
-                       lpsizes = (lpsizes & ~(0xful << (i * 4))) |
-                               (((unsigned long)psize) << (i * 4));
-       /* Assign the value back */
-       mm->context.low_slices_psize = lpsizes;
+       memset(lpsizes, (psize << 4) | psize, SLICE_NUM_LOW >> 1);
 
        hpsizes = mm->context.high_slices_psize;
-       for (i = 0; i < SLICE_NUM_HIGH; i++) {
-               mask_index = i & 0x1;
-               index = i >> 1;
-               if (((hpsizes[index] >> (mask_index * 4)) & 0xf) == old_psize)
-                       hpsizes[index] = (hpsizes[index] &
-                                         ~(0xf << (mask_index * 4))) |
-                               (((unsigned long)psize) << (mask_index * 4));
-       }
-
-
-
-
-       slice_dbg(" lsps=%lx, hsps=%lx\n",
-                 (unsigned long)mm->context.low_slices_psize,
-                 (unsigned long)mm->context.high_slices_psize);
+       memset(hpsizes, (psize << 4) | psize, SLICE_NUM_HIGH >> 1);
 
- bail:
-       spin_unlock_irqrestore(&slice_convert_lock, flags);
+       /*
+        * Slice mask cache starts zeroed, fill the default size cache.
+        */
+       mask = slice_mask_for_size(mm, psize);
+       mask->low_slices = ~0UL;
+       if (SLICE_NUM_HIGH)
+               bitmap_fill(mask->high_slices, SLICE_NUM_HIGH);
 }
 
 void slice_set_range_psize(struct mm_struct *mm, unsigned long start,
@@ -725,7 +764,7 @@ void slice_set_range_psize(struct mm_struct *mm, unsigned long start,
        VM_BUG_ON(radix_enabled());
 
        slice_range_to_mask(start, len, &mask);
-       slice_convert(mm, mask, psize);
+       slice_convert(mm, &mask, psize);
 }
 
 #ifdef CONFIG_HUGETLB_PAGE
@@ -748,33 +787,27 @@ void slice_set_range_psize(struct mm_struct *mm, unsigned long start,
  * for now as we only use slices with hugetlbfs enabled. This should
  * be fixed as the generic code gets fixed.
  */
-int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr,
+int slice_is_hugepage_only_range(struct mm_struct *mm, unsigned long addr,
                           unsigned long len)
 {
-       struct slice_mask mask, available;
+       const struct slice_mask *maskp;
        unsigned int psize = mm->context.user_psize;
-       unsigned long high_limit = mm->context.slb_addr_limit;
 
-       if (radix_enabled())
-               return 0;
+       VM_BUG_ON(radix_enabled());
 
-       slice_range_to_mask(addr, len, &mask);
-       slice_mask_for_size(mm, psize, &available, high_limit);
+       maskp = slice_mask_for_size(mm, psize);
 #ifdef CONFIG_PPC_64K_PAGES
        /* We need to account for 4k slices too */
        if (psize == MMU_PAGE_64K) {
-               struct slice_mask compat_mask;
-               slice_mask_for_size(mm, MMU_PAGE_4K, &compat_mask, high_limit);
-               slice_or_mask(&available, &compat_mask);
+               const struct slice_mask *compat_maskp;
+               struct slice_mask available;
+
+               compat_maskp = slice_mask_for_size(mm, MMU_PAGE_4K);
+               slice_or_mask(&available, maskp, compat_maskp);
+               return !slice_check_range_fits(mm, &available, addr, len);
        }
 #endif
 
-#if 0 /* too verbose */
-       slice_dbg("is_hugepage_only_range(mm=%p, addr=%lx, len=%lx)\n",
-                mm, addr, len);
-       slice_print_mask(" mask", mask);
-       slice_print_mask(" available", available);
-#endif
-       return !slice_check_fit(mm, mask, available);
+       return !slice_check_range_fits(mm, maskp, addr, len);
 }
 #endif