1
2
3
4
5
6
7
8#include <linux/mman.h>
9#include <linux/pagemap.h>
10
11
12
13
14
15
16static long madvise_behavior(struct vm_area_struct * vma, unsigned long start,
17 unsigned long end, int behavior)
18{
19 struct mm_struct * mm = vma->vm_mm;
20 int error;
21
22 if (start != vma->vm_start) {
23 error = split_vma(mm, vma, start, 1);
24 if (error)
25 return -EAGAIN;
26 }
27
28 if (end != vma->vm_end) {
29 error = split_vma(mm, vma, end, 0);
30 if (error)
31 return -EAGAIN;
32 }
33
34 spin_lock(&mm->page_table_lock);
35 vma->vm_raend = 0;
36 VM_ClearReadHint(vma);
37
38 switch (behavior) {
39 case MADV_SEQUENTIAL:
40 vma->vm_flags |= VM_SEQ_READ;
41 break;
42 case MADV_RANDOM:
43 vma->vm_flags |= VM_RAND_READ;
44 break;
45 default:
46 break;
47 }
48 spin_unlock(&mm->page_table_lock);
49
50 return 0;
51}
52
53
54
55
56
57static long madvise_willneed(struct vm_area_struct * vma,
58 unsigned long start, unsigned long end)
59{
60 long error = -EBADF;
61 struct file * file;
62 unsigned long size, rlim_rss;
63
64
65 if (!vma->vm_file)
66 return error;
67 file = vma->vm_file;
68 size = (file->f_dentry->d_inode->i_size + PAGE_CACHE_SIZE - 1) >>
69 PAGE_CACHE_SHIFT;
70
71 start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
72 if (end > vma->vm_end)
73 end = vma->vm_end;
74 end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
75
76
77 error = -EIO;
78 rlim_rss = current->rlim ? current->rlim[RLIMIT_RSS].rlim_cur :
79 LONG_MAX;
80 if ((vma->vm_mm->rss + (end - start)) > rlim_rss)
81 return error;
82
83 do_page_cache_readahead(file, start, end - start);
84 return 0;
85}
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106static long madvise_dontneed(struct vm_area_struct * vma,
107 unsigned long start, unsigned long end)
108{
109 if (vma->vm_flags & VM_LOCKED)
110 return -EINVAL;
111
112 zap_page_range(vma, start, end - start);
113 return 0;
114}
115
116static long madvise_vma(struct vm_area_struct * vma, unsigned long start,
117 unsigned long end, int behavior)
118{
119 long error = -EBADF;
120
121 switch (behavior) {
122 case MADV_NORMAL:
123 case MADV_SEQUENTIAL:
124 case MADV_RANDOM:
125 error = madvise_behavior(vma, start, end, behavior);
126 break;
127
128 case MADV_WILLNEED:
129 error = madvise_willneed(vma, start, end);
130 break;
131
132 case MADV_DONTNEED:
133 error = madvise_dontneed(vma, start, end);
134 break;
135
136 default:
137 error = -EINVAL;
138 break;
139 }
140
141 return error;
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
176
177
178asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior)
179{
180 unsigned long end;
181 struct vm_area_struct * vma;
182 int unmapped_error = 0;
183 int error = -EINVAL;
184
185 down_write(¤t->mm->mmap_sem);
186
187 if (start & ~PAGE_MASK)
188 goto out;
189 len = (len + ~PAGE_MASK) & PAGE_MASK;
190 end = start + len;
191 if (end < start)
192 goto out;
193
194 error = 0;
195 if (end == start)
196 goto out;
197
198
199
200
201
202 vma = find_vma(current->mm, start);
203 for (;;) {
204
205 error = -ENOMEM;
206 if (!vma)
207 goto out;
208
209
210 if (start < vma->vm_start) {
211 unmapped_error = -ENOMEM;
212 start = vma->vm_start;
213 }
214
215
216 if (end <= vma->vm_end) {
217 if (start < end) {
218 error = madvise_vma(vma, start, end,
219 behavior);
220 if (error)
221 goto out;
222 }
223 error = unmapped_error;
224 goto out;
225 }
226
227
228 error = madvise_vma(vma, start, vma->vm_end, behavior);
229 if (error)
230 goto out;
231 start = vma->vm_end;
232 vma = vma->vm_next;
233 }
234
235out:
236 up_write(¤t->mm->mmap_sem);
237 return error;
238}
239