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/slab.h> 11#include <linux/pagemap.h> 12#include <linux/mm.h> 13#include <linux/mman.h> 14 15#include <asm/uaccess.h> 16#include <asm/pgtable.h> 17 18/* 19 * Later we can get more picky about what "in core" means precisely. 20 * For now, simply check to see if the page is in the page cache, 21 * and is up to date; i.e. that no page-in operation would be required 22 * at this time if an application were to map and access this page. 23 */ 24static unsigned char mincore_page(struct vm_area_struct * vma, 25 unsigned long pgoff) 26{ 27 unsigned char present = 0; 28 struct address_space * as = vma->vm_file->f_mapping; 29 struct page * page; 30 31 page = find_get_page(as, pgoff); 32 if (page) { 33 present = PageUptodate(page); 34 page_cache_release(page); 35 } 36 37 return present; 38} 39 40/* 41 * Do a chunk of "sys_mincore()". We've already checked 42 * all the arguments, we hold the mmap semaphore: we should 43 * just return the amount of info we're asked for. 44 */ 45static long do_mincore(unsigned long addr, unsigned char *vec, unsigned long pages) 46{ 47 unsigned long i, nr, pgoff; 48 struct vm_area_struct *vma = find_vma(current->mm, addr); 49 50 /* 51 * find_vma() didn't find anything above us, or we're 52 * in an unmapped hole in the address space: ENOMEM. 53 */ 54 if (!vma || addr < vma->vm_start) 55 return -ENOMEM; 56 57 /* 58 * Ok, got it. But check whether it's a segment we support 59 * mincore() on. Right now, we don't do any anonymous mappings. 60 * 61 * FIXME: This is just stupid. And returning ENOMEM is 62 * stupid too. We should just look at the page tables. But 63 * this is what we've traditionally done, so we'll just 64 * continue doing it. 65 */ 66 if (!vma->vm_file) 67 return -ENOMEM; 68 69 /* 70 * Calculate how many pages there are left in the vma, and 71 * what the pgoff is for our address. 72 */ 73 nr = (vma->vm_end - addr) >> PAGE_SHIFT; 74 if (nr > pages) 75 nr = pages; 76 77 pgoff = (addr - vma->vm_start) >> PAGE_SHIFT; 78 pgoff += vma->vm_pgoff; 79 80 /* And then we just fill the sucker in.. */ 81 for (i = 0 ; i < nr; i++, pgoff++) 82 vec[i] = mincore_page(vma, pgoff); 83 84 return nr; 85} 86 87/* 88 * The mincore(2) system call. 89 * 90 * mincore() returns the memory residency status of the pages in the 91 * current process's address space specified by [addr, addr + len). 92 * The status is returned in a vector of bytes. The least significant 93 * bit of each byte is 1 if the referenced page is in memory, otherwise 94 * it is zero. 95 * 96 * Because the status of a page can change after mincore() checks it 97 * but before it returns to the application, the returned vector may 98 * contain stale information. Only locked pages are guaranteed to 99 * remain in memory. 100 * 101 * return values: 102 * zero - success 103 * -EFAULT - vec points to an illegal address 104 * -EINVAL - addr is not a multiple of PAGE_CACHE_SIZE 105 * -ENOMEM - Addresses in the range [addr, addr + len] are 106 * invalid for the address space of this process, or 107 * specify one or more pages which are not currently 108 * mapped 109 * -EAGAIN - A kernel resource was temporarily unavailable. 110 */ 111asmlinkage long sys_mincore(unsigned long start, size_t len, 112 unsigned char __user * vec) 113{ 114 long retval; 115 unsigned long pages; 116 unsigned char *tmp; 117 118 /* Check the start address: needs to be page-aligned.. */ 119 if (start & ~PAGE_CACHE_MASK) 120 return -EINVAL; 121 122 /* ..and we need to be passed a valid user-space range */ 123 if (!access_ok(VERIFY_READ, (void __user *) start, len)) 124 return -ENOMEM; 125 126 /* This also avoids any overflows on PAGE_CACHE_ALIGN */ 127 pages = len >> PAGE_SHIFT; 128 pages += (len & ~PAGE_MASK) != 0; 129 130 if (!access_ok(VERIFY_WRITE, vec, pages)) 131 return -EFAULT; 132 133 tmp = (void *) __get_free_page(GFP_USER); 134 if (!tmp) 135 return -EAGAIN; 136 137 retval = 0; 138 while (pages) { 139 /* 140 * Do at most PAGE_SIZE entries per iteration, due to 141 * the temporary buffer size. 142 */ 143 down_read(¤t->mm->mmap_sem); 144 retval = do_mincore(start, tmp, min(pages, PAGE_SIZE)); 145 up_read(¤t->mm->mmap_sem); 146 147 if (retval <= 0) 148 break; 149 if (copy_to_user(vec, tmp, retval)) { 150 retval = -EFAULT; 151 break; 152 } 153 pages -= retval; 154 vec += retval; 155 start += retval << PAGE_SHIFT; 156 retval = 0; 157 } 158 free_page((unsigned long) tmp); 159 return retval; 160} 161

