RHEL4/mm/mincore.c
<<
>>
Prefs
   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(&current->mm->mmap_sem);
 144                retval = do_mincore(start, tmp, min(pages, PAGE_SIZE));
 145                up_read(&current->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