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 int write;
291 size_t len;
292
293 write = madvise_need_mmap_write(behavior);
294 if (write)
295 down_write(¤t->mm->mmap_sem);
296 else
297 down_read(¤t->mm->mmap_sem);
298
299 if (start & ~PAGE_MASK)
300 goto out;
301 len = (len_in + ~PAGE_MASK) & PAGE_MASK;
302
303
304 if (len_in && !len)
305 goto out;
306
307 end = start + len;
308 if (end < start)
309 goto out;
310
311 error = 0;
312 if (end == start)
313 goto out;
314
315
316
317
318
319
320 vma = find_vma_prev(current->mm, start, &prev);
321 if (vma && start > vma->vm_start)
322 prev = vma;
323
324 for (;;) {
325
326 error = -ENOMEM;
327 if (!vma)
328 goto out;
329
330
331 if (start < vma->vm_start) {
332 unmapped_error = -ENOMEM;
333 start = vma->vm_start;
334 if (start >= end)
335 goto out;
336 }
337
338
339 tmp = vma->vm_end;
340 if (end < tmp)
341 tmp = end;
342
343
344 error = madvise_vma(vma, &prev, start, tmp, behavior);
345 if (error)
346 goto out;
347 start = tmp;
348 if (prev && start < prev->vm_end)
349 start = prev->vm_end;
350 error = unmapped_error;
351 if (start >= end)
352 goto out;
353 if (prev)
354 vma = prev->vm_next;
355 else
356 vma = find_vma(current->mm, start);
357 }
358out:
359 if (write)
360 up_write(¤t->mm->mmap_sem);
361 else
362 up_read(¤t->mm->mmap_sem);
363
364 return error;
365}
366