2005-04-17 02:20:36 +04:00
/*
* linux / mm / mincore . c
*
2006-12-16 20:44:32 +03:00
* Copyright ( C ) 1994 - 2006 Linus Torvalds
2005-04-17 02:20:36 +04:00
*/
/*
* The mincore ( ) system call .
*/
# include <linux/slab.h>
# include <linux/pagemap.h>
# include <linux/mm.h>
# include <linux/mman.h>
# include <linux/syscalls.h>
# include <asm/uaccess.h>
# include <asm/pgtable.h>
/*
* Later we can get more picky about what " in core " means precisely .
* For now , simply check to see if the page is in the page cache ,
* and is up to date ; i . e . that no page - in operation would be required
* at this time if an application were to map and access this page .
*/
static unsigned char mincore_page ( struct vm_area_struct * vma ,
unsigned long pgoff )
{
unsigned char present = 0 ;
struct address_space * as = vma - > vm_file - > f_mapping ;
struct page * page ;
page = find_get_page ( as , pgoff ) ;
if ( page ) {
present = PageUptodate ( page ) ;
page_cache_release ( page ) ;
}
return present ;
}
2006-12-16 20:44:32 +03:00
/*
* Do a chunk of " sys_mincore() " . We ' ve already checked
* all the arguments , we hold the mmap semaphore : we should
* just return the amount of info we ' re asked for .
*/
static long do_mincore ( unsigned long addr , unsigned char * vec , unsigned long pages )
2005-04-17 02:20:36 +04:00
{
2006-12-16 20:44:32 +03:00
unsigned long i , nr , pgoff ;
struct vm_area_struct * vma = find_vma ( current - > mm , addr ) ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
/*
2006-12-17 03:01:50 +03:00
* find_vma ( ) didn ' t find anything above us , or we ' re
* in an unmapped hole in the address space : ENOMEM .
2006-12-16 20:44:32 +03:00
*/
2006-12-17 03:01:50 +03:00
if ( ! vma | | addr < vma - > vm_start )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
/*
* Ok , got it . But check whether it ' s a segment we support
* mincore ( ) on . Right now , we don ' t do any anonymous mappings .
2006-12-17 03:01:50 +03:00
*
* FIXME : This is just stupid . And returning ENOMEM is
* stupid too . We should just look at the page tables . But
* this is what we ' ve traditionally done , so we ' ll just
* continue doing it .
2006-12-16 20:44:32 +03:00
*/
if ( ! vma - > vm_file )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
/*
* Calculate how many pages there are left in the vma , and
* what the pgoff is for our address .
*/
nr = ( vma - > vm_end - addr ) > > PAGE_SHIFT ;
if ( nr > pages )
nr = pages ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
pgoff = ( addr - vma - > vm_start ) > > PAGE_SHIFT ;
pgoff + = vma - > vm_pgoff ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
/* And then we just fill the sucker in.. */
for ( i = 0 ; i < nr ; i + + , pgoff + + )
vec [ i ] = mincore_page ( vma , pgoff ) ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
return nr ;
2005-04-17 02:20:36 +04:00
}
/*
* The mincore ( 2 ) system call .
*
* mincore ( ) returns the memory residency status of the pages in the
* current process ' s address space specified by [ addr , addr + len ) .
* The status is returned in a vector of bytes . The least significant
* bit of each byte is 1 if the referenced page is in memory , otherwise
* it is zero .
*
* Because the status of a page can change after mincore ( ) checks it
* but before it returns to the application , the returned vector may
* contain stale information . Only locked pages are guaranteed to
* remain in memory .
*
* return values :
* zero - success
* - EFAULT - vec points to an illegal address
* - EINVAL - addr is not a multiple of PAGE_CACHE_SIZE
* - ENOMEM - Addresses in the range [ addr , addr + len ] are
* invalid for the address space of this process , or
* specify one or more pages which are not currently
* mapped
* - EAGAIN - A kernel resource was temporarily unavailable .
*/
asmlinkage long sys_mincore ( unsigned long start , size_t len ,
unsigned char __user * vec )
{
2006-12-16 20:44:32 +03:00
long retval ;
unsigned long pages ;
unsigned char * tmp ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
/* Check the start address: needs to be page-aligned.. */
if ( start & ~ PAGE_CACHE_MASK )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
/* ..and we need to be passed a valid user-space range */
if ( ! access_ok ( VERIFY_READ , ( void __user * ) start , len ) )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
/* This also avoids any overflows on PAGE_CACHE_ALIGN */
pages = len > > PAGE_SHIFT ;
pages + = ( len & ~ PAGE_MASK ) ! = 0 ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
if ( ! access_ok ( VERIFY_WRITE , vec , pages ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-12-16 20:44:32 +03:00
tmp = ( void * ) __get_free_page ( GFP_USER ) ;
if ( ! tmp )
2006-12-17 03:01:50 +03:00
return - EAGAIN ;
2006-12-16 20:44:32 +03:00
retval = 0 ;
while ( pages ) {
/*
* Do at most PAGE_SIZE entries per iteration , due to
* the temporary buffer size .
*/
down_read ( & current - > mm - > mmap_sem ) ;
2006-12-17 18:52:47 +03:00
retval = do_mincore ( start , tmp , min ( pages , PAGE_SIZE ) ) ;
2006-12-16 20:44:32 +03:00
up_read ( & current - > mm - > mmap_sem ) ;
if ( retval < = 0 )
break ;
if ( copy_to_user ( vec , tmp , retval ) ) {
retval = - EFAULT ;
break ;
2005-04-17 02:20:36 +04:00
}
2006-12-16 20:44:32 +03:00
pages - = retval ;
vec + = retval ;
start + = retval < < PAGE_SHIFT ;
retval = 0 ;
2005-04-17 02:20:36 +04:00
}
2006-12-16 20:44:32 +03:00
free_page ( ( unsigned long ) tmp ) ;
return retval ;
2005-04-17 02:20:36 +04:00
}