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/hugetlb.h>
12
13
14
15
16
17static long madvise_behavior(struct vm_area_struct * vma, unsigned long start,
18 unsigned long end, int behavior)
19{
20 struct mm_struct * mm = vma->vm_mm;
21 int error = 0;
22
23 if (start != vma->vm_start) {
24 error = split_vma(mm, vma, start, 1);
25 if (error)
26 goto out;
27 }
28
29 if (end != vma->vm_end) {
30 error = split_vma(mm, vma, end, 0);
31 if (error)
32 goto out;
33 }
34
35
36
37
38 VM_ClearReadHint(vma);
39
40 switch (behavior) {
41 case MADV_SEQUENTIAL:
42 vma->vm_flags |= VM_SEQ_READ;
43 break;
44 case MADV_RANDOM:
45 vma->vm_flags |= VM_RAND_READ;
46 break;
47 default:
48 break;
49 }
50
51out:
52 if (error == -ENOMEM)
53 error = -EAGAIN;
54 return error;
55}
56
57
58
59
60static long madvise_willneed(struct vm_area_struct * vma,
61 unsigned long start, unsigned long end)
62{
63 struct file *file = vma->vm_file;
64
65 if (!file)
66 return -EBADF;
67
68 start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
69 if (end > vma->vm_end)
70 end = vma->vm_end;
71 end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
72
73 force_page_cache_readahead(file->f_mapping,
74 file, start, max_sane_readahead(end - start));
75 return 0;
76}
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97static long madvise_dontneed(struct vm_area_struct * vma,
98 unsigned long start, unsigned long end)
99{
100 if ((vma->vm_flags & VM_LOCKED) || is_vm_hugetlb_page(vma))
101 return -EINVAL;
102
103 if (unlikely(vma->vm_flags & VM_NONLINEAR)) {
104 struct zap_details details = {
105 .nonlinear_vma = vma,
106 .last_index = ULONG_MAX,
107 };
108 zap_page_range(vma, start, end - start, &details);
109 } else
110 zap_page_range(vma, start, end - start, NULL);
111 return 0;
112}
113
114static long madvise_vma(struct vm_area_struct * vma, unsigned long start,
115 unsigned long end, int behavior)
116{
117 long error = -EBADF;
118
119 switch (behavior) {
120 case MADV_NORMAL:
121 case MADV_SEQUENTIAL:
122 case MADV_RANDOM:
123 error = madvise_behavior(vma, start, end, behavior);
124 break;
125
126 case MADV_WILLNEED:
127 error = madvise_willneed(vma, start, end);
128 break;
129
130 case MADV_DONTNEED:
131 error = madvise_dontneed(vma, start, end);
132 break;
133
134 default:
135 error = -EINVAL;
136 break;
137 }
138
139 return error;
140}
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176asmlinkage long sys_madvise(unsigned long start, size_t len_in, int behavior)
177{
178 unsigned long end;
179 struct vm_area_struct * vma;
180 int unmapped_error = 0;
181 int error = -EINVAL;
182 size_t len;
183
184 down_write(¤t->mm->mmap_sem);
185
186 if (start & ~PAGE_MASK)
187 goto out;
188 len = (len_in + ~PAGE_MASK) & PAGE_MASK;
189
190
191 if (len_in && !len)
192 goto out;
193
194 end = start + len;
195 if (end < start)
196 goto out;
197
198 error = 0;
199 if (end == start)
200 goto out;
201
202
203
204
205
206 vma = find_vma(current->mm, start);
207 for (;;) {
208
209 error = -ENOMEM;
210 if (!vma)
211 goto out;
212
213
214 if (start < vma->vm_start) {
215 unmapped_error = -ENOMEM;
216 start = vma->vm_start;
217 }
218
219
220 if (end <= vma->vm_end) {
221 if (start < end) {
222 error = madvise_vma(vma, start, end,
223 behavior);
224 if (error)
225 goto out;
226 }
227 error = unmapped_error;
228 goto out;
229 }
230
231
232 error = madvise_vma(vma, start, vma->vm_end, behavior);
233 if (error)
234 goto out;
235 start = vma->vm_end;
236 vma = vma->vm_next;
237 }
238
239out:
240 up_write(¤t->mm->mmap_sem);
241 return error;
242}
243