blob: ff14a5044ce6ec66366f95acbffef82c732d23c5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#include <linux/errno.h>
2#include <linux/sched.h>
3#include <linux/syscalls.h>
4#include <linux/mm.h>
Alexey Dobriyan4e950f62007-07-30 02:36:13 +04005#include <linux/fs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006#include <linux/smp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007#include <linux/sem.h>
8#include <linux/msg.h>
9#include <linux/shm.h>
10#include <linux/stat.h>
11#include <linux/mman.h>
12#include <linux/file.h>
13#include <linux/utsname.h>
14#include <linux/personality.h>
Jiri Kosinacc503c12008-01-30 13:31:07 +010015#include <linux/random.h>
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -030016#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <asm/ia32.h>
Jaswinder Singhbbc1f692008-07-21 21:34:13 +053019#include <asm/syscalls.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
Jason Baron0ac676f2009-08-10 16:53:11 -040021SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
22 unsigned long, prot, unsigned long, flags,
23 unsigned long, fd, unsigned long, off)
Linus Torvalds1da177e2005-04-16 15:20:36 -070024{
25 long error;
Linus Torvalds1da177e2005-04-16 15:20:36 -070026 error = -EINVAL;
27 if (off & ~PAGE_MASK)
28 goto out;
29
Al Virof8b72562009-11-30 17:37:04 -050030 error = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
Linus Torvalds1da177e2005-04-16 15:20:36 -070031out:
32 return error;
33}
34
35static void find_start_end(unsigned long flags, unsigned long *begin,
36 unsigned long *end)
37{
Suresh Siddha84929802005-06-21 17:14:32 -070038 if (!test_thread_flag(TIF_IA32) && (flags & MAP_32BIT)) {
Jiri Kosinacc503c12008-01-30 13:31:07 +010039 unsigned long new_begin;
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 /* This is usually used needed to map code in small
41 model, so it needs to be in the first 31bit. Limit
42 it to that. This means we need to move the
43 unmapped base down for this case. This can give
44 conflicts with the heap, but we assume that glibc
45 malloc knows how to fall back to mmap. Give it 1GB
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -030046 of playground for now. -AK */
47 *begin = 0x40000000;
48 *end = 0x80000000;
Jiri Kosinacc503c12008-01-30 13:31:07 +010049 if (current->flags & PF_RANDOMIZE) {
50 new_begin = randomize_range(*begin, *begin + 0x02000000, 0);
51 if (new_begin)
52 *begin = new_begin;
53 }
Suresh Siddha84929802005-06-21 17:14:32 -070054 } else {
55 *begin = TASK_UNMAPPED_BASE;
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -030056 *end = TASK_SIZE;
Suresh Siddha84929802005-06-21 17:14:32 -070057 }
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -030058}
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60unsigned long
61arch_get_unmapped_area(struct file *filp, unsigned long addr,
62 unsigned long len, unsigned long pgoff, unsigned long flags)
63{
64 struct mm_struct *mm = current->mm;
65 struct vm_area_struct *vma;
66 unsigned long start_addr;
67 unsigned long begin, end;
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -030068
Benjamin Herrenschmidt11300a62007-05-06 14:50:11 -070069 if (flags & MAP_FIXED)
70 return addr;
71
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -030072 find_start_end(flags, &begin, &end);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74 if (len > end)
75 return -ENOMEM;
76
77 if (addr) {
78 addr = PAGE_ALIGN(addr);
79 vma = find_vma(mm, addr);
80 if (end - len >= addr &&
81 (!vma || addr + len <= vma->vm_start))
82 return addr;
83 }
Wolfgang Wander1363c3c2005-06-21 17:14:49 -070084 if (((flags & MAP_32BIT) || test_thread_flag(TIF_IA32))
85 && len <= mm->cached_hole_size) {
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -030086 mm->cached_hole_size = 0;
Wolfgang Wander1363c3c2005-06-21 17:14:49 -070087 mm->free_area_cache = begin;
88 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 addr = mm->free_area_cache;
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -030090 if (addr < begin)
91 addr = begin;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 start_addr = addr;
93
94full_search:
95 for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
96 /* At this point: (!vma || addr < vma->vm_end). */
97 if (end - len < addr) {
98 /*
99 * Start a new search - just in case we missed
100 * some holes.
101 */
102 if (start_addr != begin) {
103 start_addr = addr = begin;
Wolfgang Wander1363c3c2005-06-21 17:14:49 -0700104 mm->cached_hole_size = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 goto full_search;
106 }
107 return -ENOMEM;
108 }
109 if (!vma || addr + len <= vma->vm_start) {
110 /*
111 * Remember the place where we stopped the search:
112 */
113 mm->free_area_cache = addr + len;
114 return addr;
115 }
Wolfgang Wander1363c3c2005-06-21 17:14:49 -0700116 if (addr + mm->cached_hole_size < vma->vm_start)
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -0300117 mm->cached_hole_size = vma->vm_start - addr;
Wolfgang Wander1363c3c2005-06-21 17:14:49 -0700118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 addr = vma->vm_end;
120 }
121}
122
Jiri Kosinacc503c12008-01-30 13:31:07 +0100123
124unsigned long
125arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
126 const unsigned long len, const unsigned long pgoff,
127 const unsigned long flags)
128{
129 struct vm_area_struct *vma;
130 struct mm_struct *mm = current->mm;
131 unsigned long addr = addr0;
132
133 /* requested length too big for entire address space */
134 if (len > TASK_SIZE)
135 return -ENOMEM;
136
137 if (flags & MAP_FIXED)
138 return addr;
139
140 /* for MAP_32BIT mappings we force the legact mmap base */
141 if (!test_thread_flag(TIF_IA32) && (flags & MAP_32BIT))
142 goto bottomup;
143
144 /* requesting a specific address */
145 if (addr) {
146 addr = PAGE_ALIGN(addr);
147 vma = find_vma(mm, addr);
148 if (TASK_SIZE - len >= addr &&
149 (!vma || addr + len <= vma->vm_start))
150 return addr;
151 }
152
153 /* check if free_area_cache is useful for us */
154 if (len <= mm->cached_hole_size) {
Andrew Mortonbb1ad822008-01-30 13:31:07 +0100155 mm->cached_hole_size = 0;
156 mm->free_area_cache = mm->mmap_base;
157 }
Jiri Kosinacc503c12008-01-30 13:31:07 +0100158
159 /* either no address requested or can't fit in requested address hole */
160 addr = mm->free_area_cache;
161
162 /* make sure it can fit in the remaining address space */
163 if (addr > len) {
164 vma = find_vma(mm, addr-len);
165 if (!vma || addr <= vma->vm_start)
166 /* remember the address as a hint for next time */
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -0300167 return mm->free_area_cache = addr-len;
Jiri Kosinacc503c12008-01-30 13:31:07 +0100168 }
169
170 if (mm->mmap_base < len)
171 goto bottomup;
172
173 addr = mm->mmap_base-len;
174
175 do {
176 /*
177 * Lookup failure means no vma is above this address,
178 * else if new region fits below vma->vm_start,
179 * return with success:
180 */
181 vma = find_vma(mm, addr);
182 if (!vma || addr+len <= vma->vm_start)
183 /* remember the address as a hint for next time */
Gustavo F. Padovane9c8abb2008-07-29 02:48:56 -0300184 return mm->free_area_cache = addr;
Jiri Kosinacc503c12008-01-30 13:31:07 +0100185
Andrew Mortonbb1ad822008-01-30 13:31:07 +0100186 /* remember the largest hole we saw so far */
187 if (addr + mm->cached_hole_size < vma->vm_start)
188 mm->cached_hole_size = vma->vm_start - addr;
Jiri Kosinacc503c12008-01-30 13:31:07 +0100189
190 /* try just below the current vma->vm_start */
191 addr = vma->vm_start-len;
192 } while (len < vma->vm_start);
193
194bottomup:
195 /*
196 * A failed mmap() very likely causes application failure,
197 * so fall back to the bottom-up function here. This scenario
198 * can happen with large stack limits and large mmap()
199 * allocations.
200 */
201 mm->cached_hole_size = ~0UL;
Andrew Mortonbb1ad822008-01-30 13:31:07 +0100202 mm->free_area_cache = TASK_UNMAPPED_BASE;
Jiri Kosinacc503c12008-01-30 13:31:07 +0100203 addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
204 /*
205 * Restore the topdown base:
206 */
207 mm->free_area_cache = mm->mmap_base;
208 mm->cached_hole_size = ~0UL;
209
210 return addr;
211}