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