1
2
3
4
5
6
7
8#include <linux/mman.h>
9#include <linux/pagemap.h>
10#include <linux/syscalls.h>
11#include <linux/mempolicy.h>
12#include <linux/hugetlb.h>
13#include <linux/sched.h>
14
15
16
17
18
19
20static int madvise_need_mmap_write(int behavior)
21{
22 switch (behavior) {
23 case MADV_REMOVE:
24 case MADV_WILLNEED:
25 case MADV_DONTNEED:
26 return 0;
27 default:
28
29 return 1;
30 }
31}
32
33
34
35
36
37static long madvise_behavior(struct vm_area_struct * vma,
38 struct vm_area_struct **prev,
39 unsigned long start, unsigned long end, int behavior)
40{
41 struct mm_struct * mm = vma->vm_mm;
42 int error = 0;
43 pgoff_t pgoff;
44 int new_flags = vma->vm_flags;
45
46 switch (behavior) {
47 case MADV_NORMAL:
48 new_flags = new_flags & ~VM_RAND_READ & ~VM_SEQ_READ;
49 break;
50 case MADV_SEQUENTIAL:
51 new_flags = (new_flags & ~VM_RAND_READ) | VM_SEQ_READ;
52 break;
53 case MADV_RANDOM:
54 new_flags = (new_flags & ~VM_SEQ_READ) | VM_RAND_READ;
55 break;
56 case MADV_DONTFORK:
57 new_flags |= VM_DONTCOPY;
58 break;
59 case MADV_DOFORK:
60 new_flags &= ~VM_DONTCOPY;
61 break;
62 }
63
64 if (new_flags == vma->vm_flags) {
65 *prev = vma;
66 goto out;
67 }
68
69 pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
70 *prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma,
71 vma->vm_file, pgoff, vma_policy(vma));
72 if (*prev) {
73 vma = *prev;
74 goto success;
75 }
76
77 *prev = vma;
78
79 if (start != vma->vm_start) {
80 error = split_vma(mm, vma, start, 1);
81 if (error)
82 goto out;
83 }
84
85 if (end != vma->vm_end) {
86 error = split_vma(mm, vma, end, 0);
87 if (error)
88 goto out;
89 }
90
91success:
92
93
94
95 vma->vm_flags = new_flags;
96
97out:
98 if (error == -ENOMEM)
99 error = -EAGAIN;
100 return error;
101}
102
103
104
105
106static long madvise_willneed(struct vm_area_struct * vma,
107 struct vm_area_struct ** prev,
108 unsigned long start, unsigned long end)
109{
110 struct file *file = vma->vm_file;
111
112 if (!file)
113 return -EBADF;
114
115 if (file->f_mapping->a_ops->get_xip_page) {
116
117 return 0;
118 }
119
120 *prev = vma;
121 start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
122 if (end > vma->vm_end)
123 end = vma->vm_end;
124 end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
125
126 force_page_cache_readahead(file->f_mapping,
127 file, start, max_sane_readahead(end - start));
128 return 0;
129}
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150static long madvise_dontneed(struct vm_area_struct * vma,
151 struct vm_area_struct ** prev,
152 unsigned long start, unsigned long end)
153{
154 *prev = vma;
155 if (vma->vm_flags & (VM_LOCKED|VM_HUGETLB|VM_PFNMAP))
156 return -EINVAL;
157
158 if (unlikely(vma->vm_flags & VM_NONLINEAR)) {
159 struct zap_details details = {
160 .nonlinear_vma = vma,
161 .last_index = ULONG_MAX,
162 };
163 zap_page_range(vma, start, end - start, &details);
164 } else
165 zap_page_range(vma, start, end - start, NULL);
166 return 0;
167}
168
169
170
171
172
173
174
175
176static long madvise_remove(struct vm_area_struct *vma,
177 struct vm_area_struct **prev,
178 unsigned long start, unsigned long end)
179{
180 struct address_space *mapping;
181 loff_t offset, endoff;
182 int error;
183
184 *prev = NULL;
185
186 if (vma->vm_flags & (VM_LOCKED|VM_NONLINEAR|VM_HUGETLB))
187 return -EINVAL;
188
189 if (!vma->vm_file || !vma->vm_file->f_mapping
190 || !vma->vm_file->f_mapping->host) {
191 return -EINVAL;
192 }
193
194 if ((vma->vm_flags & (VM_SHARED|VM_WRITE)) != (VM_SHARED|VM_WRITE))
195 return -EACCES;
196
197 mapping = vma->vm_file->f_mapping;
198
199 offset = (loff_t)(start - vma->vm_start)
200 + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
201 endoff = (loff_t)(end - vma->vm_start - 1)
202 + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
203
204
205 up_read(¤t->mm->mmap_sem);
206 error = vmtruncate_range(mapping->host, offset, endoff);
207 down_read(¤t->mm->mmap_sem);
208 return error;
209}
210
211static long
212madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
213 unsigned long start, unsigned long end, int behavior)
214{
215 long error;
216
217 switch (behavior) {
218 case MADV_DOFORK:
219 if (vma->vm_flags & VM_IO) {
220 error = -EINVAL;
221 break;
222 }
223 case MADV_DONTFORK:
224 case MADV_NORMAL:
225 case MADV_SEQUENTIAL:
226 case MADV_RANDOM:
227 error = madvise_behavior(vma, prev, start, end, behavior);
228 break;
229 case MADV_REMOVE:
230 error = madvise_remove(vma, prev, start, end);
231 break;
232
233 case MADV_WILLNEED:
234 error = madvise_willneed(vma, prev, start, end);
235 break;
236
237 case MADV_DONTNEED:
238 error = madvise_dontneed(vma, prev, start, end);
239 break;
240
241 default:
242 error = -EINVAL;
243 break;
244 }
245 return error;
246}
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284asmlinkage long sys_madvise(unsigned long start, size_t len_in, int behavior)
285{
286 unsigned long end, tmp;
287 struct vm_area_struct * vma, *prev;
288 int unmapped_error = 0;
289 int error = -EINVAL;
290 size_t len;
291
292 if (madvise_need_mmap_write(behavior))
293 down_write(¤t->mm->mmap_sem);
294 else
295 down_read(¤t->mm->mmap_sem);
296
297 if (start & ~PAGE_MASK)
298 goto out;
299 len = (len_in + ~PAGE_MASK) & PAGE_MASK;
300
301
302 if (len_in && !len)
303 goto out;
304
305 end = start + len;
306 if (end < start)
307 goto out;
308
309 error = 0;
310 if (end == start)
311 goto out;
312
313
314
315
316
317
318 vma = find_vma_prev(current->mm, start, &prev);
319 if (vma && start > vma->vm_start)
320 prev = vma;
321
322 for (;;) {
323
324 error = -ENOMEM;
325 if (!vma)
326 goto out;
327
328
329 if (start < vma->vm_start) {
330 unmapped_error = -ENOMEM;
331 start = vma->vm_start;
332 if (start >= end)
333 goto out;
334 }
335
336
337 tmp = vma->vm_end;
338 if (end < tmp)
339 tmp = end;
340
341
342 error = madvise_vma(vma, &prev, start, tmp, behavior);
343 if (error)
344 goto out;
345 start = tmp;
346 if (prev && start < prev->vm_end)
347 start = prev->vm_end;
348 error = unmapped_error;
349 if (start >= end)
350 goto out;
351 if (prev)
352 vma = prev->vm_next;
353 else
354 vma = find_vma(current->mm, start);
355 }
356out:
357 if (madvise_need_mmap_write(behavior))
358 up_write(¤t->mm->mmap_sem);
359 else
360 up_read(¤t->mm->mmap_sem);
361
362 return error;
363}
364