| 1 | |
| 2 | |
| 3 | * linux/mm/mincore.c
|
| 4 | *
|
| 5 | * Copyright (C) 1994-2006 Linus Torvalds
|
| 6 |
|
| 7 |
|
| 8 | |
| 9 | * The mincore() system call.
|
| 10 |
|
| 11 | #include <linux/pagemap.h>
|
| 12 | #include <linux/gfp.h>
|
| 13 | #include <linux/pagewalk.h>
|
| 14 | #include <linux/mman.h>
|
| 15 | #include <linux/slab.h>
|
| 16 | #include <linux/syscalls.h>
|
| 17 | #include <linux/swap.h>
|
| 18 | #include <linux/leafops.h>
|
| 19 | #include <linux/shmem_fs.h>
|
| 20 | #include <linux/hugetlb.h>
|
| 21 | #include <linux/pgtable.h>
|
| 22 |
|
| 23 | #include <linux/uaccess.h>
|
| 24 | #include "swap.h"
|
| 25 | #include "internal.h"
|
| 26 |
|
| 27 | static int mincore_hugetlb(pte_t *pte, unsigned long hmask, unsigned long addr,
|
| 28 | unsigned long end, struct mm_walk *walk)
|
| 29 | {
|
| 30 | #ifdef CONFIG_HUGETLB_PAGE
|
| 31 | const unsigned long nr = (end - addr) >> PAGE_SHIFT;
|
| 32 | unsigned char resident;
|
| 33 | spinlock_t *ptl;
|
| 34 | pte_t ptep;
|
| 35 |
|
| 36 | ptl = huge_pte_lock(hstate_vma(walk->vma), walk->mm, pte);
|
| 37 | ptep = huge_ptep_get(walk->mm, addr, pte);
|
| 38 | resident = !huge_pte_none(ptep) && !pte_is_marker(ptep);
|
| 39 | memset(walk->private, resident, nr);
|
| 40 | walk->private += nr;
|
| 41 | spin_unlock(ptl);
|
| 42 | #else
|
| 43 | BUG();
|
| 44 | #endif
|
| 45 | return 0;
|
| 46 | }
|
| 47 |
|
| 48 | static unsigned char mincore_swap(swp_entry_t entry, bool shmem)
|
| 49 | {
|
| 50 | struct swap_info_struct *si;
|
| 51 | struct folio *folio = NULL;
|
| 52 | unsigned char present = 0;
|
| 53 |
|
| 54 | |
| 55 | * Shmem mapping may contain swapin error entries, which are
|
| 56 | * absent. Page table may contain migration or hwpoison
|
| 57 | * entries which are always uptodate.
|
| 58 |
|
| 59 | if (!softleaf_is_swap(entry))
|
| 60 | return !shmem;
|
| 61 |
|
| 62 | if (!IS_ENABLED(CONFIG_SWAP)) {
|
| 63 | WARN_ON(1);
|
| 64 | return 0;
|
| 65 | }
|
| 66 |
|
| 67 | |
| 68 | * Shmem mapping lookup is lockless, so we need to grab the swap
|
| 69 | * device. mincore page table walk locks the PTL, and the swap
|
| 70 | * device is stable, avoid touching the si for better performance.
|
| 71 |
|
| 72 | if (shmem) {
|
| 73 | si = get_swap_device(entry);
|
| 74 | if (!si)
|
| 75 | return 0;
|
| 76 | }
|
| 77 | folio = swap_cache_get_folio(entry);
|
| 78 | if (shmem)
|
| 79 | put_swap_device(si);
|
| 80 | if (folio) {
|
| 81 | present = folio_test_uptodate(folio);
|
| 82 | folio_put(folio);
|
| 83 | }
|
| 84 |
|
| 85 | return present;
|
| 86 | }
|
| 87 |
|
| 88 | |
| 89 | * Later we can get more picky about what "in core" means precisely.
|
| 90 | * For now, simply check to see if the page is in the page cache,
|
| 91 | * and is up to date; i.e. that no page-in operation would be required
|
| 92 | * at this time if an application were to map and access this page.
|
| 93 |
|
| 94 | static unsigned char mincore_page(struct address_space *mapping, pgoff_t index)
|
| 95 | {
|
| 96 | unsigned char present;
|
| 97 | struct folio *folio;
|
| 98 |
|
| 99 | |
| 100 | * When tmpfs swaps out a page from a file, any process mapping that
|
| 101 | * file will not get a swp_entry_t in its pte, but rather it is like
|
| 102 | * any other file mapping (ie. marked !present and faulted in with
|
| 103 | * tmpfs's .fault). So swapped out tmpfs mappings are tested here.
|
| 104 |
|
| 105 | folio = filemap_get_entry(mapping, index);
|
| 106 | if (!folio)
|
| 107 | return 0;
|
| 108 |
|
| 109 | if (xa_is_value(folio)) {
|
| 110 | if (!shmem_mapping(mapping))
|
| 111 | return 0;
|
| 112 | return mincore_swap(radix_to_swp_entry(folio), true);
|
| 113 | }
|
| 114 | present = folio_test_uptodate(folio);
|
| 115 | folio_put(folio);
|
| 116 |
|
| 117 | return present;
|
| 118 | }
|
| 119 |
|
| 120 | static int __mincore_unmapped_range(unsigned long addr, unsigned long end,
|
| 121 | struct vm_area_struct *vma, unsigned char *vec)
|
| 122 | {
|
| 123 | unsigned long nr = (end - addr) >> PAGE_SHIFT;
|
| 124 | int i;
|
| 125 |
|
| 126 | if (vma->vm_file) {
|
| 127 | pgoff_t pgoff;
|
| 128 |
|
| 129 | pgoff = linear_page_index(vma, addr);
|
| 130 | for (i = 0; i < nr; i++, pgoff++)
|
| 131 | vec[i] = mincore_page(vma->vm_file->f_mapping, pgoff);
|
| 132 | } else {
|
| 133 | for (i = 0; i < nr; i++)
|
| 134 | vec[i] = 0;
|
| 135 | }
|
| 136 | return nr;
|
| 137 | }
|
| 138 |
|
| 139 | static int mincore_unmapped_range(unsigned long addr, unsigned long end,
|
| 140 | __always_unused int depth,
|
| 141 | struct mm_walk *walk)
|
| 142 | {
|
| 143 | walk->private += __mincore_unmapped_range(addr, end,
|
| 144 | walk->vma, walk->private);
|
| 145 | return 0;
|
| 146 | }
|
| 147 |
|
| 148 | static int mincore_pud_entry(pud_t *pudp, unsigned long addr, unsigned long end,
|
| 149 | struct mm_walk *walk)
|
| 150 | {
|
| 151 | if (pud_is_huge(pudp_get(pudp))) {
|
| 152 | const unsigned long nr = (end - addr) >> PAGE_SHIFT;
|
| 153 |
|
| 154 | memset(walk->private, 1, nr);
|
| 155 | walk->private += nr;
|
| 156 | walk->action = ACTION_CONTINUE;
|
| 157 | }
|
| 158 |
|
| 159 | return 0;
|
| 160 | }
|
| 161 |
|
| 162 | static int mincore_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
|
| 163 | struct mm_walk *walk)
|
| 164 | {
|
| 165 | spinlock_t *ptl;
|
| 166 | struct vm_area_struct *vma = walk->vma;
|
| 167 | pte_t *ptep;
|
| 168 | unsigned char *vec = walk->private;
|
| 169 | int nr = (end - addr) >> PAGE_SHIFT;
|
| 170 | int step, i;
|
| 171 |
|
| 172 | ptl = pmd_trans_huge_lock(pmd, vma);
|
| 173 | if (ptl) {
|
| 174 | memset(vec, 1, nr);
|
| 175 | spin_unlock(ptl);
|
| 176 | goto out;
|
| 177 | }
|
| 178 |
|
| 179 | ptep = pte_offset_map_lock(walk->mm, pmd, addr, &ptl);
|
| 180 | if (!ptep) {
|
| 181 | walk->action = ACTION_AGAIN;
|
| 182 | return 0;
|
| 183 | }
|
| 184 | for (; addr != end; ptep += step, addr += step * PAGE_SIZE) {
|
| 185 | pte_t pte = ptep_get(ptep);
|
| 186 |
|
| 187 | step = 1;
|
| 188 |
|
| 189 | if (pte_none(pte) || pte_is_marker(pte))
|
| 190 | __mincore_unmapped_range(addr, addr + PAGE_SIZE,
|
| 191 | vma, vec);
|
| 192 | else if (pte_present(pte)) {
|
| 193 | unsigned int batch = pte_batch_hint(ptep, pte);
|
| 194 |
|
| 195 | if (batch > 1) {
|
| 196 | unsigned int max_nr = (end - addr) >> PAGE_SHIFT;
|
| 197 |
|
| 198 | step = min_t(unsigned int, batch, max_nr);
|
| 199 | }
|
| 200 |
|
| 201 | for (i = 0; i < step; i++)
|
| 202 | vec[i] = 1;
|
| 203 | } else {
|
| 204 | const softleaf_t entry = softleaf_from_pte(pte);
|
| 205 |
|
| 206 | *vec = mincore_swap(entry, false);
|
| 207 | }
|
| 208 | vec += step;
|
| 209 | }
|
| 210 | pte_unmap_unlock(ptep - 1, ptl);
|
| 211 | out:
|
| 212 | walk->private += nr;
|
| 213 | cond_resched();
|
| 214 | return 0;
|
| 215 | }
|
| 216 |
|
| 217 | static inline bool can_do_mincore(struct vm_area_struct *vma)
|
| 218 | {
|
| 219 | if (vma_is_anonymous(vma))
|
| 220 | return true;
|
| 221 | if (!vma->vm_file)
|
| 222 | return false;
|
| 223 | |
| 224 | * Reveal pagecache information only for non-anonymous mappings that
|
| 225 | * correspond to the files the calling process could (if tried) open
|
| 226 | * for writing; otherwise we'd be including shared non-exclusive
|
| 227 | * mappings, which opens a side channel.
|
| 228 |
|
| 229 | return file_owner_or_capable(vma->vm_file) ||
|
| 230 | file_permission(vma->vm_file, MAY_WRITE) == 0;
|
| 231 | }
|
| 232 |
|
| 233 | static const struct mm_walk_ops mincore_walk_ops = {
|
| 234 | .pud_entry = mincore_pud_entry,
|
| 235 | .pmd_entry = mincore_pte_range,
|
| 236 | .pte_hole = mincore_unmapped_range,
|
| 237 | .hugetlb_entry = mincore_hugetlb,
|
| 238 | .walk_lock = PGWALK_RDLOCK,
|
| 239 | };
|
| 240 |
|
| 241 | |
| 242 | * Do a chunk of "sys_mincore()". We've already checked
|
| 243 | * all the arguments, we hold the mmap semaphore: we should
|
| 244 | * just return the amount of info we're asked for.
|
| 245 |
|
| 246 | static long do_mincore(unsigned long addr, unsigned long pages, unsigned char *vec)
|
| 247 | {
|
| 248 | struct vm_area_struct *vma;
|
| 249 | unsigned long end;
|
| 250 | int err;
|
| 251 |
|
| 252 | vma = vma_lookup(current->mm, addr);
|
| 253 | if (!vma)
|
| 254 | return -ENOMEM;
|
| 255 | end = min(vma->vm_end, addr + (pages << PAGE_SHIFT));
|
| 256 | if (!can_do_mincore(vma)) {
|
| 257 | unsigned long pages = DIV_ROUND_UP(end - addr, PAGE_SIZE);
|
| 258 | memset(vec, 1, pages);
|
| 259 | return pages;
|
| 260 | }
|
| 261 |
|
| 262 | err = walk_page_range_vma(vma, addr, end, &mincore_walk_ops, vec);
|
| 263 | if (err < 0)
|
| 264 | return err;
|
| 265 | return (end - addr) >> PAGE_SHIFT;
|
| 266 | }
|
| 267 |
|
| 268 | |
| 269 | * The mincore(2) system call.
|
| 270 | *
|
| 271 | * mincore() returns the memory residency status of the pages in the
|
| 272 | * current process's address space specified by [addr, addr + len).
|
| 273 | * The status is returned in a vector of bytes. The least significant
|
| 274 | * bit of each byte is 1 if the referenced page is in memory, otherwise
|
| 275 | * it is zero.
|
| 276 | *
|
| 277 | * Because the status of a page can change after mincore() checks it
|
| 278 | * but before it returns to the application, the returned vector may
|
| 279 | * contain stale information. Only locked pages are guaranteed to
|
| 280 | * remain in memory.
|
| 281 | *
|
| 282 | * return values:
|
| 283 | * zero - success
|
| 284 | * -EFAULT - vec points to an illegal address
|
| 285 | * -EINVAL - addr is not a multiple of PAGE_SIZE
|
| 286 | * -ENOMEM - Addresses in the range [addr, addr + len] are
|
| 287 | * invalid for the address space of this process, or
|
| 288 | * specify one or more pages which are not currently
|
| 289 | * mapped
|
| 290 | * -EAGAIN - A kernel resource was temporarily unavailable.
|
| 291 |
|
| 292 | SYSCALL_DEFINE3(mincore, unsigned long, start, size_t, len,
|
| 293 | unsigned char __user *, vec)
|
| 294 | {
|
| 295 | long retval;
|
| 296 | unsigned long pages;
|
| 297 | unsigned char *tmp;
|
| 298 |
|
| 299 | start = untagged_addr(start);
|
| 300 |
|
| 301 |
|
| 302 | if (unlikely(start & ~PAGE_MASK))
|
| 303 | return -EINVAL;
|
| 304 |
|
| 305 |
|
| 306 | if (!access_ok((void __user *) start, len))
|
| 307 | return -ENOMEM;
|
| 308 |
|
| 309 |
|
| 310 | pages = len >> PAGE_SHIFT;
|
| 311 | pages += (offset_in_page(len)) != 0;
|
| 312 |
|
| 313 | if (!access_ok(vec, pages))
|
| 314 | return -EFAULT;
|
| 315 |
|
| 316 | tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
| 317 | if (!tmp)
|
| 318 | return -EAGAIN;
|
| 319 |
|
| 320 | retval = 0;
|
| 321 | while (pages) {
|
| 322 | |
| 323 | * Do at most PAGE_SIZE entries per iteration, due to
|
| 324 | * the temporary buffer size.
|
| 325 |
|
| 326 | mmap_read_lock(current->mm);
|
| 327 | retval = do_mincore(start, min(pages, PAGE_SIZE), tmp);
|
| 328 | mmap_read_unlock(current->mm);
|
| 329 |
|
| 330 | if (retval <= 0)
|
| 331 | break;
|
| 332 | if (copy_to_user(vec, tmp, retval)) {
|
| 333 | retval = -EFAULT;
|
| 334 | break;
|
| 335 | }
|
| 336 | pages -= retval;
|
| 337 | vec += retval;
|
| 338 | start += retval << PAGE_SHIFT;
|
| 339 | retval = 0;
|
| 340 | }
|
| 341 | kfree(tmp);
|
| 342 | return retval;
|
| 343 | }
|