diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2006-03-17 17:41:03 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-03-20 04:16:35 -0500 |
commit | a91690ddd05ab0b7fbdd37733875525ac75c20f2 (patch) | |
tree | f6937c0ce29c79078d6df8346a374a7b9947f360 | |
parent | b5e7ae5dd034c2c0ed75c31fca04a805097817bc (diff) |
[SPARC64]: Top-down address space allocation for 32-bit tasks.
Currently allocations are very constrained for 32-bit processes.
It grows down-up from 0x70000000 to 0xf0000000 which gives about
2GB of stack + dynamic mmap() space.
So support the top-down method, and we need to override the
generic helper function in order to deal with D-cache coloring.
With these changes I was able to squeeze out a mmap() just over
3.6GB in size in a 32-bit process.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | arch/sparc64/kernel/binfmt_aout32.c | 2 | ||||
-rw-r--r-- | arch/sparc64/kernel/sys_sparc.c | 190 | ||||
-rw-r--r-- | include/asm-sparc64/pgtable.h | 5 | ||||
-rw-r--r-- | include/asm-sparc64/processor.h | 2 |
4 files changed, 189 insertions, 10 deletions
diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c index cb9ecd0172c0..d7caa60a0074 100644 --- a/arch/sparc64/kernel/binfmt_aout32.c +++ b/arch/sparc64/kernel/binfmt_aout32.c | |||
@@ -239,6 +239,8 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) | |||
239 | (current->mm->start_data = N_DATADDR(ex)); | 239 | (current->mm->start_data = N_DATADDR(ex)); |
240 | current->mm->brk = ex.a_bss + | 240 | current->mm->brk = ex.a_bss + |
241 | (current->mm->start_brk = N_BSSADDR(ex)); | 241 | (current->mm->start_brk = N_BSSADDR(ex)); |
242 | current->mm->free_area_cache = current->mm->mmap_base; | ||
243 | current->mm->cached_hole_size = 0; | ||
242 | 244 | ||
243 | current->mm->mmap = NULL; | 245 | current->mm->mmap = NULL; |
244 | compute_creds(bprm); | 246 | compute_creds(bprm); |
diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 8840415408be..61dffb9349bd 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c | |||
@@ -82,9 +82,34 @@ static inline int straddles_64bit_va_hole(unsigned long start, unsigned long end | |||
82 | return 1; | 82 | return 1; |
83 | } | 83 | } |
84 | 84 | ||
85 | #define COLOUR_ALIGN(addr,pgoff) \ | 85 | /* These functions differ from the default implementations in |
86 | ((((addr)+SHMLBA-1)&~(SHMLBA-1)) + \ | 86 | * mm/mmap.c in two ways: |
87 | (((pgoff)<<PAGE_SHIFT) & (SHMLBA-1))) | 87 | * |
88 | * 1) For file backed MAP_SHARED mmap()'s we D-cache color align, | ||
89 | * for fixed such mappings we just validate what the user gave us. | ||
90 | * 2) For 64-bit tasks we avoid mapping anything within 4GB of | ||
91 | * the spitfire/niagara VA-hole. | ||
92 | */ | ||
93 | |||
94 | static inline unsigned long COLOUR_ALIGN(unsigned long addr, | ||
95 | unsigned long pgoff) | ||
96 | { | ||
97 | unsigned long base = (addr+SHMLBA-1)&~(SHMLBA-1); | ||
98 | unsigned long off = (pgoff<<PAGE_SHIFT) & (SHMLBA-1); | ||
99 | |||
100 | return base + off; | ||
101 | } | ||
102 | |||
103 | static inline unsigned long COLOUR_ALIGN_DOWN(unsigned long addr, | ||
104 | unsigned long pgoff) | ||
105 | { | ||
106 | unsigned long base = addr & ~(SHMLBA-1); | ||
107 | unsigned long off = (pgoff<<PAGE_SHIFT) & (SHMLBA-1); | ||
108 | |||
109 | if (base + off <= addr) | ||
110 | return base + off; | ||
111 | return base - off; | ||
112 | } | ||
88 | 113 | ||
89 | unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) | 114 | unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) |
90 | { | 115 | { |
@@ -106,7 +131,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi | |||
106 | 131 | ||
107 | if (test_thread_flag(TIF_32BIT)) | 132 | if (test_thread_flag(TIF_32BIT)) |
108 | task_size = 0xf0000000UL; | 133 | task_size = 0xf0000000UL; |
109 | if (len > task_size || len >= VA_EXCLUDE_START) | 134 | if (unlikely(len > task_size || len >= VA_EXCLUDE_START)) |
110 | return -ENOMEM; | 135 | return -ENOMEM; |
111 | 136 | ||
112 | do_color_align = 0; | 137 | do_color_align = 0; |
@@ -125,11 +150,12 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi | |||
125 | return addr; | 150 | return addr; |
126 | } | 151 | } |
127 | 152 | ||
128 | if (len <= mm->cached_hole_size) { | 153 | if (len > mm->cached_hole_size) { |
154 | start_addr = addr = mm->free_area_cache; | ||
155 | } else { | ||
156 | start_addr = addr = TASK_UNMAPPED_BASE; | ||
129 | mm->cached_hole_size = 0; | 157 | mm->cached_hole_size = 0; |
130 | mm->free_area_cache = TASK_UNMAPPED_BASE; | ||
131 | } | 158 | } |
132 | start_addr = addr = mm->free_area_cache; | ||
133 | 159 | ||
134 | task_size -= len; | 160 | task_size -= len; |
135 | 161 | ||
@@ -146,7 +172,7 @@ full_search: | |||
146 | addr = VA_EXCLUDE_END; | 172 | addr = VA_EXCLUDE_END; |
147 | vma = find_vma(mm, VA_EXCLUDE_END); | 173 | vma = find_vma(mm, VA_EXCLUDE_END); |
148 | } | 174 | } |
149 | if (task_size < addr) { | 175 | if (unlikely(task_size < addr)) { |
150 | if (start_addr != TASK_UNMAPPED_BASE) { | 176 | if (start_addr != TASK_UNMAPPED_BASE) { |
151 | start_addr = addr = TASK_UNMAPPED_BASE; | 177 | start_addr = addr = TASK_UNMAPPED_BASE; |
152 | mm->cached_hole_size = 0; | 178 | mm->cached_hole_size = 0; |
@@ -154,7 +180,7 @@ full_search: | |||
154 | } | 180 | } |
155 | return -ENOMEM; | 181 | return -ENOMEM; |
156 | } | 182 | } |
157 | if (!vma || addr + len <= vma->vm_start) { | 183 | if (likely(!vma || addr + len <= vma->vm_start)) { |
158 | /* | 184 | /* |
159 | * Remember the place where we stopped the search: | 185 | * Remember the place where we stopped the search: |
160 | */ | 186 | */ |
@@ -170,6 +196,121 @@ full_search: | |||
170 | } | 196 | } |
171 | } | 197 | } |
172 | 198 | ||
199 | unsigned long | ||
200 | arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, | ||
201 | const unsigned long len, const unsigned long pgoff, | ||
202 | const unsigned long flags) | ||
203 | { | ||
204 | struct vm_area_struct *vma; | ||
205 | struct mm_struct *mm = current->mm; | ||
206 | unsigned long task_size = 0xf0000000UL; | ||
207 | unsigned long addr = addr0; | ||
208 | int do_color_align; | ||
209 | |||
210 | /* This should only ever run for 32-bit processes. */ | ||
211 | BUG_ON(!test_thread_flag(TIF_32BIT)); | ||
212 | |||
213 | if (flags & MAP_FIXED) { | ||
214 | /* We do not accept a shared mapping if it would violate | ||
215 | * cache aliasing constraints. | ||
216 | */ | ||
217 | if ((flags & MAP_SHARED) && | ||
218 | ((addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1))) | ||
219 | return -EINVAL; | ||
220 | return addr; | ||
221 | } | ||
222 | |||
223 | if (unlikely(len > task_size)) | ||
224 | return -ENOMEM; | ||
225 | |||
226 | do_color_align = 0; | ||
227 | if (filp || (flags & MAP_SHARED)) | ||
228 | do_color_align = 1; | ||
229 | |||
230 | /* requesting a specific address */ | ||
231 | if (addr) { | ||
232 | if (do_color_align) | ||
233 | addr = COLOUR_ALIGN(addr, pgoff); | ||
234 | else | ||
235 | addr = PAGE_ALIGN(addr); | ||
236 | |||
237 | vma = find_vma(mm, addr); | ||
238 | if (task_size - len >= addr && | ||
239 | (!vma || addr + len <= vma->vm_start)) | ||
240 | return addr; | ||
241 | } | ||
242 | |||
243 | /* check if free_area_cache is useful for us */ | ||
244 | if (len <= mm->cached_hole_size) { | ||
245 | mm->cached_hole_size = 0; | ||
246 | mm->free_area_cache = mm->mmap_base; | ||
247 | } | ||
248 | |||
249 | /* either no address requested or can't fit in requested address hole */ | ||
250 | addr = mm->free_area_cache; | ||
251 | if (do_color_align) { | ||
252 | unsigned long base = COLOUR_ALIGN_DOWN(addr-len, pgoff); | ||
253 | |||
254 | addr = base + len; | ||
255 | } | ||
256 | |||
257 | /* make sure it can fit in the remaining address space */ | ||
258 | if (likely(addr > len)) { | ||
259 | vma = find_vma(mm, addr-len); | ||
260 | if (!vma || addr <= vma->vm_start) { | ||
261 | /* remember the address as a hint for next time */ | ||
262 | return (mm->free_area_cache = addr-len); | ||
263 | } | ||
264 | } | ||
265 | |||
266 | if (unlikely(mm->mmap_base < len)) | ||
267 | goto bottomup; | ||
268 | |||
269 | addr = mm->mmap_base-len; | ||
270 | if (do_color_align) | ||
271 | addr = COLOUR_ALIGN_DOWN(addr, pgoff); | ||
272 | |||
273 | do { | ||
274 | /* | ||
275 | * Lookup failure means no vma is above this address, | ||
276 | * else if new region fits below vma->vm_start, | ||
277 | * return with success: | ||
278 | */ | ||
279 | vma = find_vma(mm, addr); | ||
280 | if (likely(!vma || addr+len <= vma->vm_start)) { | ||
281 | /* remember the address as a hint for next time */ | ||
282 | return (mm->free_area_cache = addr); | ||
283 | } | ||
284 | |||
285 | /* remember the largest hole we saw so far */ | ||
286 | if (addr + mm->cached_hole_size < vma->vm_start) | ||
287 | mm->cached_hole_size = vma->vm_start - addr; | ||
288 | |||
289 | /* try just below the current vma->vm_start */ | ||
290 | addr = vma->vm_start-len; | ||
291 | if (do_color_align) | ||
292 | addr = COLOUR_ALIGN_DOWN(addr, pgoff); | ||
293 | } while (likely(len < vma->vm_start)); | ||
294 | |||
295 | bottomup: | ||
296 | /* | ||
297 | * A failed mmap() very likely causes application failure, | ||
298 | * so fall back to the bottom-up function here. This scenario | ||
299 | * can happen with large stack limits and large mmap() | ||
300 | * allocations. | ||
301 | */ | ||
302 | mm->cached_hole_size = ~0UL; | ||
303 | mm->free_area_cache = TASK_UNMAPPED_BASE; | ||
304 | addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags); | ||
305 | /* | ||
306 | * Restore the topdown base: | ||
307 | */ | ||
308 | mm->free_area_cache = mm->mmap_base; | ||
309 | mm->cached_hole_size = ~0UL; | ||
310 | |||
311 | return addr; | ||
312 | } | ||
313 | |||
173 | /* Try to align mapping such that we align it as much as possible. */ | 314 | /* Try to align mapping such that we align it as much as possible. */ |
174 | unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, unsigned long len, unsigned long pgoff, unsigned long flags) | 315 | unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, unsigned long len, unsigned long pgoff, unsigned long flags) |
175 | { | 316 | { |
@@ -213,6 +354,37 @@ unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, u | |||
213 | return addr; | 354 | return addr; |
214 | } | 355 | } |
215 | 356 | ||
357 | /* Essentially the same as PowerPC... */ | ||
358 | void arch_pick_mmap_layout(struct mm_struct *mm) | ||
359 | { | ||
360 | /* | ||
361 | * Fall back to the standard layout if the personality | ||
362 | * bit is set, or if the expected stack growth is unlimited: | ||
363 | */ | ||
364 | if (!test_thread_flag(TIF_32BIT) || | ||
365 | (current->personality & ADDR_COMPAT_LAYOUT) || | ||
366 | current->signal->rlim[RLIMIT_STACK].rlim_cur == RLIM_INFINITY || | ||
367 | sysctl_legacy_va_layout) { | ||
368 | mm->mmap_base = TASK_UNMAPPED_BASE; | ||
369 | mm->get_unmapped_area = arch_get_unmapped_area; | ||
370 | mm->unmap_area = arch_unmap_area; | ||
371 | } else { | ||
372 | /* We know it's 32-bit */ | ||
373 | unsigned long task_size = 0xf0000000UL; | ||
374 | unsigned long gap; | ||
375 | |||
376 | gap = current->signal->rlim[RLIMIT_STACK].rlim_cur; | ||
377 | if (gap < 128 * 1024 * 1024) | ||
378 | gap = 128 * 1024 * 1024; | ||
379 | if (gap > (task_size / 6 * 5)) | ||
380 | gap = (task_size / 6 * 5); | ||
381 | |||
382 | mm->mmap_base = task_size - (gap & PAGE_MASK); | ||
383 | mm->get_unmapped_area = arch_get_unmapped_area_topdown; | ||
384 | mm->unmap_area = arch_unmap_area_topdown; | ||
385 | } | ||
386 | } | ||
387 | |||
216 | asmlinkage unsigned long sparc_brk(unsigned long brk) | 388 | asmlinkage unsigned long sparc_brk(unsigned long brk) |
217 | { | 389 | { |
218 | /* People could try to be nasty and use ta 0x6d in 32bit programs */ | 390 | /* People could try to be nasty and use ta 0x6d in 32bit programs */ |
diff --git a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h index d427ce649214..ed4124edf837 100644 --- a/include/asm-sparc64/pgtable.h +++ b/include/asm-sparc64/pgtable.h | |||
@@ -752,8 +752,11 @@ extern int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long from, | |||
752 | 752 | ||
753 | #include <asm-generic/pgtable.h> | 753 | #include <asm-generic/pgtable.h> |
754 | 754 | ||
755 | /* We provide our own get_unmapped_area to cope with VA holes for userland */ | 755 | /* We provide our own get_unmapped_area to cope with VA holes and |
756 | * SHM area cache aliasing for userland. | ||
757 | */ | ||
756 | #define HAVE_ARCH_UNMAPPED_AREA | 758 | #define HAVE_ARCH_UNMAPPED_AREA |
759 | #define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN | ||
757 | 760 | ||
758 | /* We provide a special get_unmapped_area for framebuffer mmaps to try and use | 761 | /* We provide a special get_unmapped_area for framebuffer mmaps to try and use |
759 | * the largest alignment possible such that larget PTEs can be used. | 762 | * the largest alignment possible such that larget PTEs can be used. |
diff --git a/include/asm-sparc64/processor.h b/include/asm-sparc64/processor.h index 685479fb4364..c6896b88283e 100644 --- a/include/asm-sparc64/processor.h +++ b/include/asm-sparc64/processor.h | |||
@@ -217,6 +217,8 @@ static inline void prefetchw(const void *x) | |||
217 | 217 | ||
218 | #define spin_lock_prefetch(x) prefetchw(x) | 218 | #define spin_lock_prefetch(x) prefetchw(x) |
219 | 219 | ||
220 | #define HAVE_ARCH_PICK_MMAP_LAYOUT | ||
221 | |||
220 | #endif /* !(__ASSEMBLY__) */ | 222 | #endif /* !(__ASSEMBLY__) */ |
221 | 223 | ||
222 | #endif /* !(__ASM_SPARC64_PROCESSOR_H) */ | 224 | #endif /* !(__ASM_SPARC64_PROCESSOR_H) */ |