1
2
3
4
5
6#include <linux/stat.h>
7#include <linux/sched.h>
8#include <linux/kernel.h>
9#include <linux/mm.h>
10#include <linux/slab.h>
11#include <linux/shm.h>
12#include <linux/errno.h>
13#include <linux/mman.h>
14#include <linux/string.h>
15#include <linux/smp.h>
16#include <linux/smp_lock.h>
17
18#include <asm/uaccess.h>
19#include <asm/system.h>
20#include <asm/pgtable.h>
21
22static inline int mlock_fixup_all(struct vm_area_struct * vma, int newflags)
23{
24 vma->vm_flags = newflags;
25 return 0;
26}
27
28static inline int mlock_fixup_start(struct vm_area_struct * vma,
29 unsigned long end, int newflags)
30{
31 struct vm_area_struct * n;
32
33 n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
34 if (!n)
35 return -EAGAIN;
36 *n = *vma;
37 vma->vm_start = end;
38 n->vm_end = end;
39 vma->vm_offset += vma->vm_start - n->vm_start;
40 n->vm_flags = newflags;
41 n->vm_dentry = dget(vma->vm_dentry);
42 if (n->vm_ops && n->vm_ops->open)
43 n->vm_ops->open(n);
44 insert_vm_struct(current->mm, n);
45 return 0;
46}
47
48static inline int mlock_fixup_end(struct vm_area_struct * vma,
49 unsigned long start, int newflags)
50{
51 struct vm_area_struct * n;
52
53 n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
54 if (!n)
55 return -EAGAIN;
56 *n = *vma;
57 vma->vm_end = start;
58 n->vm_start = start;
59 n->vm_offset += n->vm_start - vma->vm_start;
60 n->vm_flags = newflags;
61 n->vm_dentry = dget(vma->vm_dentry);
62 if (n->vm_ops && n->vm_ops->open)
63 n->vm_ops->open(n);
64 insert_vm_struct(current->mm, n);
65 return 0;
66}
67
68static inline int mlock_fixup_middle(struct vm_area_struct * vma,
69 unsigned long start, unsigned long end, int newflags)
70{
71 struct vm_area_struct * left, * right;
72
73 left = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
74 if (!left)
75 return -EAGAIN;
76 right = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
77 if (!right) {
78 kmem_cache_free(vm_area_cachep, left);
79 return -EAGAIN;
80 }
81 *left = *vma;
82 *right = *vma;
83 left->vm_end = start;
84 vma->vm_start = start;
85 vma->vm_end = end;
86 right->vm_start = end;
87 vma->vm_offset += vma->vm_start - left->vm_start;
88 right->vm_offset += right->vm_start - left->vm_start;
89 vma->vm_flags = newflags;
90 if (vma->vm_dentry)
91 vma->vm_dentry->d_count += 2;
92
93 if (vma->vm_ops && vma->vm_ops->open) {
94 vma->vm_ops->open(left);
95 vma->vm_ops->open(right);
96 }
97 insert_vm_struct(current->mm, left);
98 insert_vm_struct(current->mm, right);
99 return 0;
100}
101
102static int mlock_fixup(struct vm_area_struct * vma,
103 unsigned long start, unsigned long end, unsigned int newflags)
104{
105 int pages, retval;
106
107 if (newflags == vma->vm_flags)
108 return 0;
109
110 if (start == vma->vm_start) {
111 if (end == vma->vm_end)
112 retval = mlock_fixup_all(vma, newflags);
113 else
114 retval = mlock_fixup_start(vma, end, newflags);
115 } else {
116 if (end == vma->vm_end)
117 retval = mlock_fixup_end(vma, start, newflags);
118 else
119 retval = mlock_fixup_middle(vma, start, end, newflags);
120 }
121 if (!retval) {
122
123 pages = (end - start) >> PAGE_SHIFT;
124 if (!(newflags & VM_LOCKED))
125 pages = -pages;
126 vma->vm_mm->locked_vm += pages;
127
128 if (newflags & VM_LOCKED)
129 while (start < end) {
130 char c;
131 get_user(c,(char *) start);
132 __asm__ __volatile__("": :"r" (c));
133 start += PAGE_SIZE;
134 }
135 }
136 return retval;
137}
138
139static int do_mlock(unsigned long start, size_t len, int on)
140{
141 unsigned long nstart, end, tmp;
142 struct vm_area_struct * vma, * next;
143 int error;
144
145 if (!suser())
146 return -EPERM;
147 len = (len + ~PAGE_MASK) & PAGE_MASK;
148 end = start + len;
149 if (end < start)
150 return -EINVAL;
151 if (end == start)
152 return 0;
153 vma = find_vma(current->mm, start);
154 if (!vma || vma->vm_start > start)
155 return -ENOMEM;
156
157 for (nstart = start ; ; ) {
158 unsigned int newflags;
159
160
161
162 newflags = vma->vm_flags | VM_LOCKED;
163 if (!on)
164 newflags &= ~VM_LOCKED;
165
166 if (vma->vm_end >= end) {
167 error = mlock_fixup(vma, nstart, end, newflags);
168 break;
169 }
170
171 tmp = vma->vm_end;
172 next = vma->vm_next;
173 error = mlock_fixup(vma, nstart, tmp, newflags);
174 if (error)
175 break;
176 nstart = tmp;
177 vma = next;
178 if (!vma || vma->vm_start != nstart) {
179 error = -ENOMEM;
180 break;
181 }
182 }
183 merge_segments(current->mm, start, end);
184 return error;
185}
186
187asmlinkage int sys_mlock(unsigned long start, size_t len)
188{
189 unsigned long locked;
190 unsigned long lock_limit;
191 int error = -ENOMEM;
192
193 lock_kernel();
194 len = (len + (start & ~PAGE_MASK) + ~PAGE_MASK) & PAGE_MASK;
195 start &= PAGE_MASK;
196
197 locked = len >> PAGE_SHIFT;
198 locked += current->mm->locked_vm;
199
200 lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
201 lock_limit >>= PAGE_SHIFT;
202
203
204 if (locked > lock_limit)
205 goto out;
206
207
208
209 if (locked > num_physpages/2)
210 goto out;
211
212 error = do_mlock(start, len, 1);
213out:
214 unlock_kernel();
215 return error;
216}
217
218asmlinkage int sys_munlock(unsigned long start, size_t len)
219{
220 int ret;
221
222 lock_kernel();
223 len = (len + (start & ~PAGE_MASK) + ~PAGE_MASK) & PAGE_MASK;
224 start &= PAGE_MASK;
225 ret = do_mlock(start, len, 0);
226 unlock_kernel();
227 return ret;
228}
229
230static int do_mlockall(int flags)
231{
232 int error;
233 unsigned int def_flags;
234 struct vm_area_struct * vma;
235
236 if (!suser())
237 return -EPERM;
238
239 def_flags = 0;
240 if (flags & MCL_FUTURE)
241 def_flags = VM_LOCKED;
242 current->mm->def_flags = def_flags;
243
244 error = 0;
245 for (vma = current->mm->mmap; vma ; vma = vma->vm_next) {
246 unsigned int newflags;
247
248 newflags = vma->vm_flags | VM_LOCKED;
249 if (!(flags & MCL_CURRENT))
250 newflags &= ~VM_LOCKED;
251 error = mlock_fixup(vma, vma->vm_start, vma->vm_end, newflags);
252 if (error)
253 break;
254 }
255 merge_segments(current->mm, 0, TASK_SIZE);
256 return error;
257}
258
259asmlinkage int sys_mlockall(int flags)
260{
261 unsigned long lock_limit;
262 int ret = -EINVAL;
263
264 lock_kernel();
265 if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE)))
266 goto out;
267
268 lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur;
269 lock_limit >>= PAGE_SHIFT;
270
271 ret = -ENOMEM;
272 if (current->mm->total_vm > lock_limit)
273 goto out;
274
275
276
277 if (current->mm->total_vm > num_physpages/2)
278 goto out;
279
280 ret = do_mlockall(flags);
281out:
282 unlock_kernel();
283 return ret;
284}
285
286asmlinkage int sys_munlockall(void)
287{
288 int ret;
289
290 lock_kernel();
291 ret = do_mlockall(0);
292 unlock_kernel();
293 return ret;
294}
295