diff options
Diffstat (limited to 'arch/powerpc/mm/slice.c')
| -rw-r--r-- | arch/powerpc/mm/slice.c | 219 | 
1 files changed, 91 insertions, 128 deletions
diff --git a/arch/powerpc/mm/slice.c b/arch/powerpc/mm/slice.c index cf9dada734b..3e99c149271 100644 --- a/arch/powerpc/mm/slice.c +++ b/arch/powerpc/mm/slice.c @@ -237,134 +237,112 @@ static void slice_convert(struct mm_struct *mm, struct slice_mask mask, int psiz  #endif  } +/* + * Compute which slice addr is part of; + * set *boundary_addr to the start or end boundary of that slice + * (depending on 'end' parameter); + * return boolean indicating if the slice is marked as available in the + * 'available' slice_mark. + */ +static bool slice_scan_available(unsigned long addr, +				 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)); +	} else { +		slice = GET_HIGH_SLICE_INDEX(addr); +		*boundary_addr = (slice + end) ? +			((slice + end) << SLICE_HIGH_SHIFT) : SLICE_LOW_TOP; +		return !!(available.high_slices & (1u << slice)); +	} +} +  static unsigned long slice_find_area_bottomup(struct mm_struct *mm,  					      unsigned long len,  					      struct slice_mask available, -					      int psize, int use_cache) +					      int psize)  { -	struct vm_area_struct *vma; -	unsigned long start_addr, addr; -	struct slice_mask mask;  	int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT); +	unsigned long addr, found, next_end; +	struct vm_unmapped_area_info info; -	if (use_cache) { -		if (len <= mm->cached_hole_size) { -			start_addr = addr = TASK_UNMAPPED_BASE; -			mm->cached_hole_size = 0; -		} else -			start_addr = addr = mm->free_area_cache; -	} else -		start_addr = addr = TASK_UNMAPPED_BASE; +	info.flags = 0; +	info.length = len; +	info.align_mask = PAGE_MASK & ((1ul << pshift) - 1); +	info.align_offset = 0; -full_search: -	for (;;) { -		addr = _ALIGN_UP(addr, 1ul << pshift); -		if ((TASK_SIZE - len) < addr) -			break; -		vma = find_vma(mm, addr); -		BUG_ON(vma && (addr >= vma->vm_end)); - -		mask = slice_range_to_mask(addr, len); -		if (!slice_check_fit(mask, available)) { -			if (addr < SLICE_LOW_TOP) -				addr = _ALIGN_UP(addr + 1,  1ul << SLICE_LOW_SHIFT); -			else -				addr = _ALIGN_UP(addr + 1,  1ul << SLICE_HIGH_SHIFT); +	addr = TASK_UNMAPPED_BASE; +	while (addr < TASK_SIZE) { +		info.low_limit = addr; +		if (!slice_scan_available(addr, available, 1, &addr))  			continue; + + next_slice: +		/* +		 * At this point [info.low_limit; addr) covers +		 * available slices only and ends at a slice boundary. +		 * 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; +		else if (slice_scan_available(addr, available, 1, &next_end)) { +			addr = next_end; +			goto next_slice;  		} -		if (!vma || addr + len <= vma->vm_start) { -			/* -			 * Remember the place where we stopped the search: -			 */ -			if (use_cache) -				mm->free_area_cache = addr + len; -			return addr; -		} -		if (use_cache && (addr + mm->cached_hole_size) < vma->vm_start) -		        mm->cached_hole_size = vma->vm_start - addr; -		addr = vma->vm_end; -	} +		info.high_limit = addr; -	/* Make sure we didn't miss any holes */ -	if (use_cache && start_addr != TASK_UNMAPPED_BASE) { -		start_addr = addr = TASK_UNMAPPED_BASE; -		mm->cached_hole_size = 0; -		goto full_search; +		found = vm_unmapped_area(&info); +		if (!(found & ~PAGE_MASK)) +			return found;  	} +  	return -ENOMEM;  }  static unsigned long slice_find_area_topdown(struct mm_struct *mm,  					     unsigned long len,  					     struct slice_mask available, -					     int psize, int use_cache) +					     int psize)  { -	struct vm_area_struct *vma; -	unsigned long addr; -	struct slice_mask mask;  	int pshift = max_t(int, mmu_psize_defs[psize].shift, PAGE_SHIFT); +	unsigned long addr, found, prev; +	struct vm_unmapped_area_info info; -	/* check if free_area_cache is useful for us */ -	if (use_cache) { -		if (len <= mm->cached_hole_size) { -			mm->cached_hole_size = 0; -			mm->free_area_cache = mm->mmap_base; -		} - -		/* either no address requested or can't fit in requested -		 * address hole -		 */ -		addr = mm->free_area_cache; - -		/* make sure it can fit in the remaining address space */ -		if (addr > len) { -			addr = _ALIGN_DOWN(addr - len, 1ul << pshift); -			mask = slice_range_to_mask(addr, len); -			if (slice_check_fit(mask, available) && -			    slice_area_is_free(mm, addr, len)) -					/* remember the address as a hint for -					 * next time -					 */ -					return (mm->free_area_cache = addr); -		} -	} +	info.flags = VM_UNMAPPED_AREA_TOPDOWN; +	info.length = len; +	info.align_mask = PAGE_MASK & ((1ul << pshift) - 1); +	info.align_offset = 0;  	addr = mm->mmap_base; -	while (addr > len) { -		/* Go down by chunk size */ -		addr = _ALIGN_DOWN(addr - len, 1ul << pshift); - -		/* Check for hit with different page size */ -		mask = slice_range_to_mask(addr, len); -		if (!slice_check_fit(mask, available)) { -			if (addr < SLICE_LOW_TOP) -				addr = _ALIGN_DOWN(addr, 1ul << SLICE_LOW_SHIFT); -			else if (addr < (1ul << SLICE_HIGH_SHIFT)) -				addr = SLICE_LOW_TOP; -			else -				addr = _ALIGN_DOWN(addr, 1ul << SLICE_HIGH_SHIFT); +	while (addr > PAGE_SIZE) { +		info.high_limit = addr; +		if (!slice_scan_available(addr - 1, available, 0, &addr))  			continue; -		} + prev_slice:  		/* -		 * Lookup failure means no vma is above this address, -		 * else if new region fits below vma->vm_start, -		 * return with success: +		 * At this point [addr; info.high_limit) covers +		 * available slices only and starts at a slice boundary. +		 * Check if we need to reduce the range, or if we can +		 * extend it to cover the previous available slice.  		 */ -		vma = find_vma(mm, addr); -		if (!vma || (addr + len) <= vma->vm_start) { -			/* remember the address as a hint for next time */ -			if (use_cache) -				mm->free_area_cache = addr; -			return addr; +		if (addr < PAGE_SIZE) +			addr = PAGE_SIZE; +		else if (slice_scan_available(addr - 1, available, 0, &prev)) { +			addr = prev; +			goto prev_slice;  		} +		info.low_limit = addr; -		/* remember the largest hole we saw so far */ -		if (use_cache && (addr + mm->cached_hole_size) < vma->vm_start) -		        mm->cached_hole_size = vma->vm_start - addr; - -		/* try just below the current vma->vm_start */ -		addr = vma->vm_start; +		found = vm_unmapped_area(&info); +		if (!(found & ~PAGE_MASK)) +			return found;  	}  	/* @@ -373,28 +351,18 @@ static unsigned long slice_find_area_topdown(struct mm_struct *mm,  	 * can happen with large stack limits and large mmap()  	 * allocations.  	 */ -	addr = slice_find_area_bottomup(mm, len, available, psize, 0); - -	/* -	 * Restore the topdown base: -	 */ -	if (use_cache) { -		mm->free_area_cache = mm->mmap_base; -		mm->cached_hole_size = ~0UL; -	} - -	return addr; +	return slice_find_area_bottomup(mm, len, available, psize);  }  static unsigned long slice_find_area(struct mm_struct *mm, unsigned long len,  				     struct slice_mask mask, int psize, -				     int topdown, int use_cache) +				     int topdown)  {  	if (topdown) -		return slice_find_area_topdown(mm, len, mask, psize, use_cache); +		return slice_find_area_topdown(mm, len, mask, psize);  	else -		return slice_find_area_bottomup(mm, len, mask, psize, use_cache); +		return slice_find_area_bottomup(mm, len, mask, psize);  }  #define or_mask(dst, src)	do {			\ @@ -415,7 +383,7 @@ static unsigned long slice_find_area(struct mm_struct *mm, unsigned long len,  unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,  				      unsigned long flags, unsigned int psize, -				      int topdown, int use_cache) +				      int topdown)  {  	struct slice_mask mask = {0, 0};  	struct slice_mask good_mask; @@ -430,8 +398,8 @@ unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,  	BUG_ON(mm->task_size == 0);  	slice_dbg("slice_get_unmapped_area(mm=%p, psize=%d...\n", mm, psize); -	slice_dbg(" addr=%lx, len=%lx, flags=%lx, topdown=%d, use_cache=%d\n", -		  addr, len, flags, topdown, use_cache); +	slice_dbg(" addr=%lx, len=%lx, flags=%lx, topdown=%d\n", +		  addr, len, flags, topdown);  	if (len > mm->task_size)  		return -ENOMEM; @@ -503,8 +471,7 @@ 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, -					  use_cache); +		newaddr = slice_find_area(mm, len, good_mask, psize, topdown);  		if (newaddr != -ENOMEM) {  			/* Found within the good mask, we don't have to setup,  			 * we thus return directly @@ -536,8 +503,7 @@ 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, -				       use_cache); +		addr = slice_find_area(mm, len, good_mask, psize, topdown);  		if (addr != -ENOMEM) {  			slice_dbg(" found area at 0x%lx\n", addr);  			return addr; @@ -547,15 +513,14 @@ 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, -			       use_cache); +	addr = slice_find_area(mm, len, potential_mask, psize, topdown);  #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, use_cache); +				       topdown);  	}  #endif @@ -586,8 +551,7 @@ unsigned long arch_get_unmapped_area(struct file *filp,  				     unsigned long flags)  {  	return slice_get_unmapped_area(addr, len, flags, -				       current->mm->context.user_psize, -				       0, 1); +				       current->mm->context.user_psize, 0);  }  unsigned long arch_get_unmapped_area_topdown(struct file *filp, @@ -597,8 +561,7 @@ unsigned long arch_get_unmapped_area_topdown(struct file *filp,  					     const unsigned long flags)  {  	return slice_get_unmapped_area(addr0, len, flags, -				       current->mm->context.user_psize, -				       1, 1); +				       current->mm->context.user_psize, 1);  }  unsigned int get_slice_psize(struct mm_struct *mm, unsigned long addr)  |