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