]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - arch/powerpc/mm/slice.c
powerpc/mmap: Any hint > 128TB searches the full VA space
[linux.git] / arch / powerpc / mm / slice.c
index 2b27458902ee888d1ba3480191497c105a184e94..966b9fccfa66b4268c5421252364bf2af6d6253f 100644 (file)
 #include <asm/copro.h>
 #include <asm/hugetlb.h>
 
-/* some sanity checks */
-#if (H_PGTABLE_RANGE >> 43) > SLICE_MASK_SIZE
-#error H_PGTABLE_RANGE exceeds slice_mask high_slices size
-#endif
-
 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)
 {
-       char    *p, buf[16 + 3 + 64 + 1];
-       int     i;
-
        if (!_slice_debug)
                return;
-       p = buf;
-       for (i = 0; i < SLICE_NUM_LOW; i++)
-               *(p++) = (mask.low_slices & (1 << i)) ? '1' : '0';
-       *(p++) = ' ';
-       *(p++) = '-';
-       *(p++) = ' ';
-       for (i = 0; i < SLICE_NUM_HIGH; i++)
-               *(p++) = (mask.high_slices & (1ul << i)) ? '1' : '0';
-       *(p++) = 0;
-
-       printk(KERN_DEBUG "%s:%s\n", label, buf);
+       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_debug(fmt); } while(0)
+#define slice_dbg(fmt...) do { if (_slice_debug) pr_devel(fmt); } while (0)
 
 #else
 
@@ -76,25 +67,28 @@ static void slice_print_mask(const char *label, struct slice_mask mask) {}
 
 #endif
 
-static struct slice_mask slice_range_to_mask(unsigned long start,
-                                            unsigned long len)
+static void slice_range_to_mask(unsigned long start, unsigned long len,
+                               struct slice_mask *ret)
 {
        unsigned long end = start + len - 1;
-       struct slice_mask ret = { 0, 0 };
+
+       ret->low_slices = 0;
+       bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
 
        if (start < SLICE_LOW_TOP) {
-               unsigned long mend = min(end, SLICE_LOW_TOP);
-               unsigned long mstart = min(start, SLICE_LOW_TOP);
+               unsigned long mend = min(end, (SLICE_LOW_TOP - 1));
 
-               ret.low_slices = (1u << (GET_LOW_SLICE_INDEX(mend) + 1))
-                       - (1u << GET_LOW_SLICE_INDEX(mstart));
+               ret->low_slices = (1u << (GET_LOW_SLICE_INDEX(mend) + 1))
+                       - (1u << GET_LOW_SLICE_INDEX(start));
        }
 
-       if ((start + len) > SLICE_LOW_TOP)
-               ret.high_slices = (1ul << (GET_HIGH_SLICE_INDEX(end) + 1))
-                       - (1ul << GET_HIGH_SLICE_INDEX(start));
+       if ((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;
 
-       return ret;
+               bitmap_set(ret->high_slices, start_index, count);
+       }
 }
 
 static int slice_area_is_free(struct mm_struct *mm, unsigned long addr,
@@ -128,53 +122,60 @@ static int slice_high_has_vma(struct mm_struct *mm, unsigned long slice)
        return !slice_area_is_free(mm, start, end - start);
 }
 
-static struct slice_mask slice_mask_for_free(struct mm_struct *mm)
+static void slice_mask_for_free(struct mm_struct *mm, struct slice_mask *ret)
 {
-       struct slice_mask ret = { 0, 0 };
        unsigned long i;
 
+       ret->low_slices = 0;
+       bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
+
        for (i = 0; i < SLICE_NUM_LOW; i++)
                if (!slice_low_has_vma(mm, i))
-                       ret.low_slices |= 1u << i;
+                       ret->low_slices |= 1u << i;
 
        if (mm->task_size <= SLICE_LOW_TOP)
-               return ret;
+               return;
 
-       for (i = 0; i < SLICE_NUM_HIGH; i++)
+       for (i = 0; i < GET_HIGH_SLICE_INDEX(mm->context.addr_limit); i++)
                if (!slice_high_has_vma(mm, i))
-                       ret.high_slices |= 1ul << i;
-
-       return ret;
+                       __set_bit(i, ret->high_slices);
 }
 
-static struct slice_mask slice_mask_for_size(struct mm_struct *mm, int psize)
+static void slice_mask_for_size(struct mm_struct *mm, int psize, struct slice_mask *ret)
 {
        unsigned char *hpsizes;
        int index, mask_index;
-       struct slice_mask ret = { 0, 0 };
        unsigned long i;
        u64 lpsizes;
 
+       ret->low_slices = 0;
+       bitmap_zero(ret->high_slices, SLICE_NUM_HIGH);
+
        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;
+                       ret->low_slices |= 1u << i;
 
        hpsizes = mm->context.high_slices_psize;
-       for (i = 0; i < SLICE_NUM_HIGH; i++) {
+       for (i = 0; i < GET_HIGH_SLICE_INDEX(mm->context.addr_limit); i++) {
                mask_index = i & 0x1;
                index = i >> 1;
                if (((hpsizes[index] >> (mask_index * 4)) & 0xf) == psize)
-                       ret.high_slices |= 1ul << i;
+                       __set_bit(i, ret->high_slices);
        }
-
-       return ret;
 }
 
-static int slice_check_fit(struct slice_mask mask, struct slice_mask available)
+static int slice_check_fit(struct mm_struct *mm,
+                          struct slice_mask mask, struct slice_mask available)
 {
+       DECLARE_BITMAP(result, SLICE_NUM_HIGH);
+       unsigned long slice_count = GET_HIGH_SLICE_INDEX(mm->context.addr_limit);
+
+       bitmap_and(result, mask.high_slices,
+                  available.high_slices, slice_count);
+
        return (mask.low_slices & available.low_slices) == mask.low_slices &&
-               (mask.high_slices & available.high_slices) == mask.high_slices;
+               bitmap_equal(result, mask.high_slices, slice_count);
 }
 
 static void slice_flush_segments(void *parm)
@@ -185,7 +186,7 @@ static void slice_flush_segments(void *parm)
        if (mm != current->active_mm)
                return;
 
-       copy_mm_to_paca(&current->active_mm->context);
+       copy_mm_to_paca(current->active_mm);
 
        local_irq_save(flags);
        slb_flush_and_rebolt();
@@ -218,18 +219,18 @@ static void slice_convert(struct mm_struct *mm, struct slice_mask mask, int psiz
        mm->context.low_slices_psize = lpsizes;
 
        hpsizes = mm->context.high_slices_psize;
-       for (i = 0; i < SLICE_NUM_HIGH; i++) {
+       for (i = 0; i < GET_HIGH_SLICE_INDEX(mm->context.addr_limit); i++) {
                mask_index = i & 0x1;
                index = i >> 1;
-               if (mask.high_slices & (1ul << i))
+               if (test_bit(i, mask.high_slices))
                        hpsizes[index] = (hpsizes[index] &
                                          ~(0xf << (mask_index * 4))) |
                                (((unsigned long)psize) << (mask_index * 4));
        }
 
        slice_dbg(" lsps=%lx, hsps=%lx\n",
-                 mm->context.low_slices_psize,
-                 mm->context.high_slices_psize);
+                 (unsigned long)mm->context.low_slices_psize,
+                 (unsigned long)mm->context.high_slices_psize);
 
        spin_unlock_irqrestore(&slice_convert_lock, flags);
 
@@ -257,14 +258,14 @@ static bool slice_scan_available(unsigned long addr,
                slice = GET_HIGH_SLICE_INDEX(addr);
                *boundary_addr = (slice + end) ?
                        ((slice + end) << SLICE_HIGH_SHIFT) : SLICE_LOW_TOP;
-               return !!(available.high_slices & (1ul << slice));
+               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,
-                                             int psize)
+                                             int psize, unsigned long high_limit)
 {
        int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
        unsigned long addr, found, next_end;
@@ -276,7 +277,10 @@ static unsigned long slice_find_area_bottomup(struct mm_struct *mm,
        info.align_offset = 0;
 
        addr = TASK_UNMAPPED_BASE;
-       while (addr < TASK_SIZE) {
+       /*
+        * Check till the allow max value for this mmap request
+        */
+       while (addr < high_limit) {
                info.low_limit = addr;
                if (!slice_scan_available(addr, available, 1, &addr))
                        continue;
@@ -288,8 +292,8 @@ static unsigned long slice_find_area_bottomup(struct mm_struct *mm,
                 * Check if we need to reduce the range, or if we can
                 * extend it to cover the next available slice.
                 */
-               if (addr >= TASK_SIZE)
-                       addr = TASK_SIZE;
+               if (addr >= high_limit)
+                       addr = high_limit;
                else if (slice_scan_available(addr, available, 1, &next_end)) {
                        addr = next_end;
                        goto next_slice;
@@ -307,7 +311,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,
-                                            int psize)
+                                            int psize, unsigned long high_limit)
 {
        int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
        unsigned long addr, found, prev;
@@ -319,6 +323,15 @@ static unsigned long slice_find_area_topdown(struct mm_struct *mm,
        info.align_offset = 0;
 
        addr = mm->mmap_base;
+       /*
+        * If we are trying to allocate above DEFAULT_MAP_WINDOW
+        * Add the different to the mmap_base.
+        * Only for that request for which high_limit is above
+        * DEFAULT_MAP_WINDOW we should apply this.
+        */
+       if (high_limit  > DEFAULT_MAP_WINDOW)
+               addr += mm->context.addr_limit - DEFAULT_MAP_WINDOW;
+
        while (addr > PAGE_SIZE) {
                info.high_limit = addr;
                if (!slice_scan_available(addr - 1, available, 0, &addr))
@@ -350,29 +363,38 @@ static unsigned long slice_find_area_topdown(struct mm_struct *mm,
         * can happen with large stack limits and large mmap()
         * allocations.
         */
-       return slice_find_area_bottomup(mm, len, available, psize);
+       return slice_find_area_bottomup(mm, len, available, psize, high_limit);
 }
 
 
 static unsigned long slice_find_area(struct mm_struct *mm, unsigned long len,
                                     struct slice_mask mask, int psize,
-                                    int topdown)
+                                    int topdown, unsigned long high_limit)
 {
        if (topdown)
-               return slice_find_area_topdown(mm, len, mask, psize);
+               return slice_find_area_topdown(mm, len, mask, psize, high_limit);
        else
-               return slice_find_area_bottomup(mm, len, mask, psize);
+               return slice_find_area_bottomup(mm, len, mask, psize, high_limit);
 }
 
-#define or_mask(dst, src)      do {                    \
-       (dst).low_slices |= (src).low_slices;           \
-       (dst).high_slices |= (src).high_slices;         \
-} while (0)
+static inline void slice_or_mask(struct slice_mask *dst, 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);
+}
 
-#define andnot_mask(dst, src)  do {                    \
-       (dst).low_slices &= ~(src).low_slices;          \
-       (dst).high_slices &= ~(src).high_slices;        \
-} while (0)
+static inline void slice_andnot_mask(struct slice_mask *dst, struct slice_mask *src)
+{
+       DECLARE_BITMAP(result, SLICE_NUM_HIGH);
+
+       dst->low_slices &= ~src->low_slices;
+
+       bitmap_andnot(result, dst->high_slices, src->high_slices, SLICE_NUM_HIGH);
+       bitmap_copy(dst->high_slices, result, SLICE_NUM_HIGH);
+}
 
 #ifdef CONFIG_PPC_64K_PAGES
 #define MMU_PAGE_BASE  MMU_PAGE_64K
@@ -384,14 +406,43 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
                                      unsigned long flags, unsigned int psize,
                                      int topdown)
 {
-       struct slice_mask mask = {0, 0};
+       struct slice_mask mask;
        struct slice_mask good_mask;
-       struct slice_mask potential_mask = {0,0} /* silence stupid warning */;
-       struct slice_mask compat_mask = {0, 0};
+       struct slice_mask potential_mask;
+       struct slice_mask compat_mask;
        int fixed = (flags & MAP_FIXED);
        int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT);
        struct mm_struct *mm = current->mm;
        unsigned long newaddr;
+       unsigned long high_limit;
+
+       /*
+        * Check if we need to expland slice area.
+        */
+       if (unlikely(addr > mm->context.addr_limit &&
+                    mm->context.addr_limit != TASK_SIZE)) {
+               mm->context.addr_limit = TASK_SIZE;
+               on_each_cpu(slice_flush_segments, mm, 1);
+       }
+       /*
+        * This mmap request can allocate upt to 512TB
+        */
+       if (addr > DEFAULT_MAP_WINDOW)
+               high_limit = mm->context.addr_limit;
+       else
+               high_limit = DEFAULT_MAP_WINDOW;
+       /*
+        * 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);
@@ -423,7 +474,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
         */
-       good_mask = slice_mask_for_size(mm, psize);
+       slice_mask_for_size(mm, psize, &good_mask);
        slice_print_mask(" good_mask", good_mask);
 
        /*
@@ -448,22 +499,22 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
 #ifdef CONFIG_PPC_64K_PAGES
        /* If we support combo pages, we can allow 64k pages in 4k slices */
        if (psize == MMU_PAGE_64K) {
-               compat_mask = slice_mask_for_size(mm, MMU_PAGE_4K);
+               slice_mask_for_size(mm, MMU_PAGE_4K, &compat_mask);
                if (fixed)
-                       or_mask(good_mask, compat_mask);
+                       slice_or_mask(&good_mask, &compat_mask);
        }
 #endif
 
        /* First check hint if it's valid or if we have MAP_FIXED */
        if (addr != 0 || fixed) {
                /* Build a mask for the requested range */
-               mask = slice_range_to_mask(addr, len);
+               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(mask, good_mask)) {
+               if (slice_check_fit(mm, mask, good_mask)) {
                        slice_dbg(" fits good !\n");
                        return addr;
                }
@@ -471,7 +522,8 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
                /* Now let's see if we can find something in the existing
                 * slices for that size
                 */
-               newaddr = slice_find_area(mm, len, good_mask, psize, topdown);
+               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
@@ -484,11 +536,11 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
        /* We don't fit in the good mask, check what other slices are
         * empty and thus can be converted
         */
-       potential_mask = slice_mask_for_free(mm);
-       or_mask(potential_mask, good_mask);
+       slice_mask_for_free(mm, &potential_mask);
+       slice_or_mask(&potential_mask, &good_mask);
        slice_print_mask(" potential", potential_mask);
 
-       if ((addr != 0 || fixed) && slice_check_fit(mask, potential_mask)) {
+       if ((addr != 0 || fixed) && slice_check_fit(mm, mask, potential_mask)) {
                slice_dbg(" fits potential !\n");
                goto convert;
        }
@@ -503,7 +555,8 @@ 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);
+               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;
@@ -513,28 +566,29 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
        /* 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);
+       addr = slice_find_area(mm, len, potential_mask,
+                              psize, topdown, high_limit);
 
 #ifdef CONFIG_PPC_64K_PAGES
        if (addr == -ENOMEM && psize == MMU_PAGE_64K) {
                /* retry the search with 4k-page slices included */
-               or_mask(potential_mask, compat_mask);
-               addr = slice_find_area(mm, len, potential_mask, psize,
-                                      topdown);
+               slice_or_mask(&potential_mask, &compat_mask);
+               addr = slice_find_area(mm, len, potential_mask,
+                                      psize, topdown, high_limit);
        }
 #endif
 
        if (addr == -ENOMEM)
                return -ENOMEM;
 
-       mask = slice_range_to_mask(addr, len);
+       slice_range_to_mask(addr, len, &mask);
        slice_dbg(" found potential area at 0x%lx\n", addr);
        slice_print_mask(" mask", mask);
 
  convert:
-       andnot_mask(mask, good_mask);
-       andnot_mask(mask, compat_mask);
-       if (mask.low_slices || mask.high_slices) {
+       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);
                if (psize > MMU_PAGE_BASE)
                        on_each_cpu(slice_flush_segments, mm, 1);
@@ -649,8 +703,8 @@ void slice_set_user_psize(struct mm_struct *mm, unsigned int psize)
 
 
        slice_dbg(" lsps=%lx, hsps=%lx\n",
-                 mm->context.low_slices_psize,
-                 mm->context.high_slices_psize);
+                 (unsigned long)mm->context.low_slices_psize,
+                 (unsigned long)mm->context.high_slices_psize);
 
  bail:
        spin_unlock_irqrestore(&slice_convert_lock, flags);
@@ -659,9 +713,11 @@ void slice_set_user_psize(struct mm_struct *mm, unsigned int psize)
 void slice_set_range_psize(struct mm_struct *mm, unsigned long start,
                           unsigned long len, unsigned int psize)
 {
-       struct slice_mask mask = slice_range_to_mask(start, len);
+       struct slice_mask mask;
 
        VM_BUG_ON(radix_enabled());
+
+       slice_range_to_mask(start, len, &mask);
        slice_convert(mm, mask, psize);
 }
 
@@ -694,14 +750,14 @@ int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr,
        if (radix_enabled())
                return 0;
 
-       mask = slice_range_to_mask(addr, len);
-       available = slice_mask_for_size(mm, psize);
+       slice_range_to_mask(addr, len, &mask);
+       slice_mask_for_size(mm, psize, &available);
 #ifdef CONFIG_PPC_64K_PAGES
        /* We need to account for 4k slices too */
        if (psize == MMU_PAGE_64K) {
                struct slice_mask compat_mask;
-               compat_mask = slice_mask_for_size(mm, MMU_PAGE_4K);
-               or_mask(available, compat_mask);
+               slice_mask_for_size(mm, MMU_PAGE_4K, &compat_mask);
+               slice_or_mask(&available, &compat_mask);
        }
 #endif
 
@@ -711,6 +767,6 @@ int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr,
        slice_print_mask(" mask", mask);
        slice_print_mask(" available", available);
 #endif
-       return !slice_check_fit(mask, available);
+       return !slice_check_fit(mm, mask, available);
 }
 #endif