x86: work around gcc 3.4.x bug
[linux-2.6] / mm / mlock.c
1 /*
2  *      linux/mm/mlock.c
3  *
4  *  (C) Copyright 1995 Linus Torvalds
5  *  (C) Copyright 2002 Christoph Hellwig
6  */
7
8 #include <linux/capability.h>
9 #include <linux/mman.h>
10 #include <linux/mm.h>
11 #include <linux/mempolicy.h>
12 #include <linux/syscalls.h>
13 #include <linux/sched.h>
14 #include <linux/module.h>
15
16 int can_do_mlock(void)
17 {
18         if (capable(CAP_IPC_LOCK))
19                 return 1;
20         if (current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur != 0)
21                 return 1;
22         return 0;
23 }
24 EXPORT_SYMBOL(can_do_mlock);
25
26 static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev,
27         unsigned long start, unsigned long end, unsigned int newflags)
28 {
29         struct mm_struct * mm = vma->vm_mm;
30         pgoff_t pgoff;
31         int pages;
32         int ret = 0;
33
34         if (newflags == vma->vm_flags) {
35                 *prev = vma;
36                 goto out;
37         }
38
39         pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
40         *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma,
41                           vma->vm_file, pgoff, vma_policy(vma));
42         if (*prev) {
43                 vma = *prev;
44                 goto success;
45         }
46
47         *prev = vma;
48
49         if (start != vma->vm_start) {
50                 ret = split_vma(mm, vma, start, 1);
51                 if (ret)
52                         goto out;
53         }
54
55         if (end != vma->vm_end) {
56                 ret = split_vma(mm, vma, end, 0);
57                 if (ret)
58                         goto out;
59         }
60
61 success:
62         /*
63          * vm_flags is protected by the mmap_sem held in write mode.
64          * It's okay if try_to_unmap_one unmaps a page just after we
65          * set VM_LOCKED, make_pages_present below will bring it back.
66          */
67         vma->vm_flags = newflags;
68
69         /*
70          * Keep track of amount of locked VM.
71          */
72         pages = (end - start) >> PAGE_SHIFT;
73         if (newflags & VM_LOCKED) {
74                 pages = -pages;
75                 if (!(newflags & VM_IO))
76                         ret = make_pages_present(start, end);
77         }
78
79         mm->locked_vm -= pages;
80 out:
81         if (ret == -ENOMEM)
82                 ret = -EAGAIN;
83         return ret;
84 }
85
86 static int do_mlock(unsigned long start, size_t len, int on)
87 {
88         unsigned long nstart, end, tmp;
89         struct vm_area_struct * vma, * prev;
90         int error;
91
92         len = PAGE_ALIGN(len);
93         end = start + len;
94         if (end < start)
95                 return -EINVAL;
96         if (end == start)
97                 return 0;
98         vma = find_vma_prev(current->mm, start, &prev);
99         if (!vma || vma->vm_start > start)
100                 return -ENOMEM;
101
102         if (start > vma->vm_start)
103                 prev = vma;
104
105         for (nstart = start ; ; ) {
106                 unsigned int newflags;
107
108                 /* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
109
110                 newflags = vma->vm_flags | VM_LOCKED;
111                 if (!on)
112                         newflags &= ~VM_LOCKED;
113
114                 tmp = vma->vm_end;
115                 if (tmp > end)
116                         tmp = end;
117                 error = mlock_fixup(vma, &prev, nstart, tmp, newflags);
118                 if (error)
119                         break;
120                 nstart = tmp;
121                 if (nstart < prev->vm_end)
122                         nstart = prev->vm_end;
123                 if (nstart >= end)
124                         break;
125
126                 vma = prev->vm_next;
127                 if (!vma || vma->vm_start != nstart) {
128                         error = -ENOMEM;
129                         break;
130                 }
131         }
132         return error;
133 }
134
135 asmlinkage long sys_mlock(unsigned long start, size_t len)
136 {
137         unsigned long locked;
138         unsigned long lock_limit;
139         int error = -ENOMEM;
140
141         if (!can_do_mlock())
142                 return -EPERM;
143
144         down_write(&current->mm->mmap_sem);
145         len = PAGE_ALIGN(len + (start & ~PAGE_MASK));
146         start &= PAGE_MASK;
147
148         locked = len >> PAGE_SHIFT;
149         locked += current->mm->locked_vm;
150
151         lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
152         lock_limit >>= PAGE_SHIFT;
153
154         /* check against resource limits */
155         if ((locked <= lock_limit) || capable(CAP_IPC_LOCK))
156                 error = do_mlock(start, len, 1);
157         up_write(&current->mm->mmap_sem);
158         return error;
159 }
160
161 asmlinkage long sys_munlock(unsigned long start, size_t len)
162 {
163         int ret;
164
165         down_write(&current->mm->mmap_sem);
166         len = PAGE_ALIGN(len + (start & ~PAGE_MASK));
167         start &= PAGE_MASK;
168         ret = do_mlock(start, len, 0);
169         up_write(&current->mm->mmap_sem);
170         return ret;
171 }
172
173 static int do_mlockall(int flags)
174 {
175         struct vm_area_struct * vma, * prev = NULL;
176         unsigned int def_flags = 0;
177
178         if (flags & MCL_FUTURE)
179                 def_flags = VM_LOCKED;
180         current->mm->def_flags = def_flags;
181         if (flags == MCL_FUTURE)
182                 goto out;
183
184         for (vma = current->mm->mmap; vma ; vma = prev->vm_next) {
185                 unsigned int newflags;
186
187                 newflags = vma->vm_flags | VM_LOCKED;
188                 if (!(flags & MCL_CURRENT))
189                         newflags &= ~VM_LOCKED;
190
191                 /* Ignore errors */
192                 mlock_fixup(vma, &prev, vma->vm_start, vma->vm_end, newflags);
193         }
194 out:
195         return 0;
196 }
197
198 asmlinkage long sys_mlockall(int flags)
199 {
200         unsigned long lock_limit;
201         int ret = -EINVAL;
202
203         if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE)))
204                 goto out;
205
206         ret = -EPERM;
207         if (!can_do_mlock())
208                 goto out;
209
210         down_write(&current->mm->mmap_sem);
211
212         lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
213         lock_limit >>= PAGE_SHIFT;
214
215         ret = -ENOMEM;
216         if (!(flags & MCL_CURRENT) || (current->mm->total_vm <= lock_limit) ||
217             capable(CAP_IPC_LOCK))
218                 ret = do_mlockall(flags);
219         up_write(&current->mm->mmap_sem);
220 out:
221         return ret;
222 }
223
224 asmlinkage long sys_munlockall(void)
225 {
226         int ret;
227
228         down_write(&current->mm->mmap_sem);
229         ret = do_mlockall(0);
230         up_write(&current->mm->mmap_sem);
231         return ret;
232 }
233
234 /*
235  * Objects with different lifetime than processes (SHM_LOCK and SHM_HUGETLB
236  * shm segments) get accounted against the user_struct instead.
237  */
238 static DEFINE_SPINLOCK(shmlock_user_lock);
239
240 int user_shm_lock(size_t size, struct user_struct *user)
241 {
242         unsigned long lock_limit, locked;
243         int allowed = 0;
244
245         locked = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
246         lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
247         if (lock_limit == RLIM_INFINITY)
248                 allowed = 1;
249         lock_limit >>= PAGE_SHIFT;
250         spin_lock(&shmlock_user_lock);
251         if (!allowed &&
252             locked + user->locked_shm > lock_limit && !capable(CAP_IPC_LOCK))
253                 goto out;
254         get_uid(user);
255         user->locked_shm += locked;
256         allowed = 1;
257 out:
258         spin_unlock(&shmlock_user_lock);
259         return allowed;
260 }
261
262 void user_shm_unlock(size_t size, struct user_struct *user)
263 {
264         spin_lock(&shmlock_user_lock);
265         user->locked_shm -= (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
266         spin_unlock(&shmlock_user_lock);
267         free_uid(user);
268 }