Branch data Line data Source code
1 : : /*
2 : : * linux/mm/mincore.c
3 : : *
4 : : * Copyright (C) 1994-2006 Linus Torvalds
5 : : */
6 : :
7 : : /*
8 : : * The mincore() system call.
9 : : */
10 : : #include <linux/pagemap.h>
11 : : #include <linux/gfp.h>
12 : : #include <linux/mm.h>
13 : : #include <linux/mman.h>
14 : : #include <linux/syscalls.h>
15 : : #include <linux/swap.h>
16 : : #include <linux/swapops.h>
17 : : #include <linux/hugetlb.h>
18 : :
19 : : #include <asm/uaccess.h>
20 : : #include <asm/pgtable.h>
21 : :
22 : : static void mincore_hugetlb_page_range(struct vm_area_struct *vma,
23 : : unsigned long addr, unsigned long end,
24 : : unsigned char *vec)
25 : : {
26 : : #ifdef CONFIG_HUGETLB_PAGE
27 : : struct hstate *h;
28 : :
29 : : h = hstate_vma(vma);
30 : : while (1) {
31 : : unsigned char present;
32 : : pte_t *ptep;
33 : : /*
34 : : * Huge pages are always in RAM for now, but
35 : : * theoretically it needs to be checked.
36 : : */
37 : : ptep = huge_pte_offset(current->mm,
38 : : addr & huge_page_mask(h));
39 : : present = ptep && !huge_pte_none(huge_ptep_get(ptep));
40 : : while (1) {
41 : : *vec = present;
42 : : vec++;
43 : : addr += PAGE_SIZE;
44 : : if (addr == end)
45 : : return;
46 : : /* check hugepage border */
47 : : if (!(addr & ~huge_page_mask(h)))
48 : : break;
49 : : }
50 : : }
51 : : #else
52 : : BUG();
53 : : #endif
54 : : }
55 : :
56 : : /*
57 : : * Later we can get more picky about what "in core" means precisely.
58 : : * For now, simply check to see if the page is in the page cache,
59 : : * and is up to date; i.e. that no page-in operation would be required
60 : : * at this time if an application were to map and access this page.
61 : : */
62 : 0 : static unsigned char mincore_page(struct address_space *mapping, pgoff_t pgoff)
63 : : {
64 : : unsigned char present = 0;
65 : : struct page *page;
66 : :
67 : : /*
68 : : * When tmpfs swaps out a page from a file, any process mapping that
69 : : * file will not get a swp_entry_t in its pte, but rather it is like
70 : : * any other file mapping (ie. marked !present and faulted in with
71 : : * tmpfs's .fault). So swapped out tmpfs mappings are tested here.
72 : : */
73 : 4 : page = find_get_page(mapping, pgoff);
74 : : #ifdef CONFIG_SWAP
75 : : /* shmem/tmpfs may return swap: account for swapcache page too. */
76 [ - + ]: 4 : if (radix_tree_exceptional_entry(page)) {
77 : : swp_entry_t swap = radix_to_swp_entry(page);
78 : 0 : page = find_get_page(swap_address_space(swap), swap.val);
79 : : }
80 : : #endif
81 [ + - ]: 4 : if (page) {
82 : 0 : present = PageUptodate(page);
83 : 4 : page_cache_release(page);
84 : : }
85 : :
86 : 4 : return present;
87 : : }
88 : :
89 : 0 : static void mincore_unmapped_range(struct vm_area_struct *vma,
90 : : unsigned long addr, unsigned long end,
91 : : unsigned char *vec)
92 : : {
93 : 4 : unsigned long nr = (end - addr) >> PAGE_SHIFT;
94 : : int i;
95 : :
96 [ - + ]: 4 : if (vma->vm_file) {
97 : : pgoff_t pgoff;
98 : :
99 : : pgoff = linear_page_index(vma, addr);
100 [ + + ]: 8 : for (i = 0; i < nr; i++, pgoff++)
101 : 4 : vec[i] = mincore_page(vma->vm_file->f_mapping, pgoff);
102 : : } else {
103 [ # # ]: 0 : for (i = 0; i < nr; i++)
104 : 0 : vec[i] = 0;
105 : : }
106 : 0 : }
107 : :
108 : 0 : static void mincore_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
109 : : unsigned long addr, unsigned long end,
110 : : unsigned char *vec)
111 : : {
112 : : unsigned long next;
113 : : spinlock_t *ptl;
114 : : pte_t *ptep;
115 : :
116 : 3 : ptep = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
117 : : do {
118 : 11 : pte_t pte = *ptep;
119 : : pgoff_t pgoff;
120 : :
121 : 11 : next = addr + PAGE_SIZE;
122 [ + + ]: 11 : if (pte_none(pte))
123 : 4 : mincore_unmapped_range(vma, addr, next, vec);
124 [ + + ]: 7 : else if (pte_present(pte))
125 : 4 : *vec = 1;
126 [ - - ]: 3 : else if (pte_file(pte)) {
127 : 0 : pgoff = pte_to_pgoff(pte);
128 : 0 : *vec = mincore_page(vma->vm_file->f_mapping, pgoff);
129 : : } else { /* pte is a swap entry */
130 : : swp_entry_t entry = pte_to_swp_entry(pte);
131 : :
132 [ # # ]: 0 : if (is_migration_entry(entry)) {
133 : : /* migration entries are always uptodate */
134 : 0 : *vec = 1;
135 : : } else {
136 : : #ifdef CONFIG_SWAP
137 : : pgoff = entry.val;
138 : 0 : *vec = mincore_page(swap_address_space(entry),
139 : : pgoff);
140 : : #else
141 : : WARN_ON(1);
142 : : *vec = 1;
143 : : #endif
144 : : }
145 : : }
146 : 8 : vec++;
147 [ + + ]: 8 : } while (ptep++, addr = next, addr != end);
148 : 3 : pte_unmap_unlock(ptep - 1, ptl);
149 : 3 : }
150 : :
151 : 0 : static void mincore_pmd_range(struct vm_area_struct *vma, pud_t *pud,
152 : : unsigned long addr, unsigned long end,
153 : : unsigned char *vec)
154 : : {
155 : : unsigned long next;
156 : 3 : pmd_t *pmd;
157 : :
158 : : pmd = pmd_offset(pud, addr);
159 : : do {
160 : : next = pmd_addr_end(addr, end);
161 : : if (pmd_trans_huge(*pmd)) {
162 : : if (mincore_huge_pmd(vma, pmd, addr, next, vec)) {
163 : : vec += (next - addr) >> PAGE_SHIFT;
164 : : continue;
165 : : }
166 : : /* fall through */
167 : : }
168 [ - + ]: 3 : if (pmd_none_or_trans_huge_or_clear_bad(pmd))
169 : 0 : mincore_unmapped_range(vma, addr, next, vec);
170 : : else
171 : 3 : mincore_pte_range(vma, pmd, addr, next, vec);
172 : : vec += (next - addr) >> PAGE_SHIFT;
173 : : } while (pmd++, addr = next, addr != end);
174 : 3 : }
175 : :
176 : : static void mincore_pud_range(struct vm_area_struct *vma, pgd_t *pgd,
177 : : unsigned long addr, unsigned long end,
178 : : unsigned char *vec)
179 : : {
180 : : unsigned long next;
181 : : pud_t *pud;
182 : :
183 : : pud = pud_offset(pgd, addr);
184 : : do {
185 : : next = pud_addr_end(addr, end);
186 : : if (pud_none_or_clear_bad(pud))
187 : : mincore_unmapped_range(vma, addr, next, vec);
188 : : else
189 : 3 : mincore_pmd_range(vma, pud, addr, next, vec);
190 : : vec += (next - addr) >> PAGE_SHIFT;
191 : : } while (pud++, addr = next, addr != end);
192 : : }
193 : :
194 : 0 : static void mincore_page_range(struct vm_area_struct *vma,
195 : : unsigned long addr, unsigned long end,
196 : : unsigned char *vec)
197 : : {
198 : : unsigned long next;
199 : : pgd_t *pgd;
200 : :
201 : 3 : pgd = pgd_offset(vma->vm_mm, addr);
202 : : do {
203 [ + - ]: 3 : next = pgd_addr_end(addr, end);
204 : : if (pgd_none_or_clear_bad(pgd))
205 : : mincore_unmapped_range(vma, addr, next, vec);
206 : : else
207 : : mincore_pud_range(vma, pgd, addr, next, vec);
208 : 3 : vec += (next - addr) >> PAGE_SHIFT;
209 [ - + ]: 6 : } while (pgd++, addr = next, addr != end);
210 : 3 : }
211 : :
212 : : /*
213 : : * Do a chunk of "sys_mincore()". We've already checked
214 : : * all the arguments, we hold the mmap semaphore: we should
215 : : * just return the amount of info we're asked for.
216 : : */
217 : 0 : static long do_mincore(unsigned long addr, unsigned long pages, unsigned char *vec)
218 : : {
219 : : struct vm_area_struct *vma;
220 : : unsigned long end;
221 : :
222 : 4 : vma = find_vma(current->mm, addr);
223 [ + - ][ + + ]: 4 : if (!vma || addr < vma->vm_start)
224 : : return -ENOMEM;
225 : :
226 : 3 : end = min(vma->vm_end, addr + (pages << PAGE_SHIFT));
227 : :
228 : : if (is_vm_hugetlb_page(vma)) {
229 : : mincore_hugetlb_page_range(vma, addr, end, vec);
230 : : return (end - addr) >> PAGE_SHIFT;
231 : : }
232 : :
233 : : end = pmd_addr_end(addr, end);
234 : :
235 : : if (is_vm_hugetlb_page(vma))
236 : : mincore_hugetlb_page_range(vma, addr, end, vec);
237 : : else
238 : 3 : mincore_page_range(vma, addr, end, vec);
239 : :
240 : 3 : return (end - addr) >> PAGE_SHIFT;
241 : : }
242 : :
243 : : /*
244 : : * The mincore(2) system call.
245 : : *
246 : : * mincore() returns the memory residency status of the pages in the
247 : : * current process's address space specified by [addr, addr + len).
248 : : * The status is returned in a vector of bytes. The least significant
249 : : * bit of each byte is 1 if the referenced page is in memory, otherwise
250 : : * it is zero.
251 : : *
252 : : * Because the status of a page can change after mincore() checks it
253 : : * but before it returns to the application, the returned vector may
254 : : * contain stale information. Only locked pages are guaranteed to
255 : : * remain in memory.
256 : : *
257 : : * return values:
258 : : * zero - success
259 : : * -EFAULT - vec points to an illegal address
260 : : * -EINVAL - addr is not a multiple of PAGE_CACHE_SIZE
261 : : * -ENOMEM - Addresses in the range [addr, addr + len] are
262 : : * invalid for the address space of this process, or
263 : : * specify one or more pages which are not currently
264 : : * mapped
265 : : * -EAGAIN - A kernel resource was temporarily unavailable.
266 : : */
267 : 0 : SYSCALL_DEFINE3(mincore, unsigned long, start, size_t, len,
268 : : unsigned char __user *, vec)
269 : : {
270 : : long retval;
271 : : unsigned long pages;
272 : : unsigned char *tmp;
273 : :
274 : : /* Check the start address: needs to be page-aligned.. */
275 [ + + ]: 4 : if (start & ~PAGE_CACHE_MASK)
276 : : return -EINVAL;
277 : :
278 : : /* ..and we need to be passed a valid user-space range */
279 [ + - ]: 3 : if (!access_ok(VERIFY_READ, (void __user *) start, len))
280 : : return -ENOMEM;
281 : :
282 : : /* This also avoids any overflows on PAGE_CACHE_ALIGN */
283 : 3 : pages = len >> PAGE_SHIFT;
284 : 3 : pages += (len & ~PAGE_MASK) != 0;
285 : :
286 [ + - ]: 3 : if (!access_ok(VERIFY_WRITE, vec, pages))
287 : : return -EFAULT;
288 : :
289 : 3 : tmp = (void *) __get_free_page(GFP_USER);
290 [ + - ]: 3 : if (!tmp)
291 : : return -EAGAIN;
292 : :
293 : : retval = 0;
294 [ + + ]: 5 : while (pages) {
295 : : /*
296 : : * Do at most PAGE_SIZE entries per iteration, due to
297 : : * the temporary buffer size.
298 : : */
299 : 4 : down_read(¤t->mm->mmap_sem);
300 : 4 : retval = do_mincore(start, min(pages, PAGE_SIZE), tmp);
301 : 4 : up_read(¤t->mm->mmap_sem);
302 : :
303 [ + + ]: 4 : if (retval <= 0)
304 : : break;
305 [ + + ]: 3 : if (copy_to_user(vec, tmp, retval)) {
306 : : retval = -EFAULT;
307 : : break;
308 : : }
309 : 2 : pages -= retval;
310 : 2 : vec += retval;
311 : 2 : start += retval << PAGE_SHIFT;
312 : : retval = 0;
313 : : }
314 : 3 : free_page((unsigned long) tmp);
315 : : return retval;
316 : : }
|