kernels/linux-2.6.24/mm/filemap.c
/**
 * do_generic_mapping_read - generic file read routine
 * @mapping:	address_space to be read
 * @ra:		file's readahead state
 * @filp:	the file to read
 * @ppos:	current file position
 * @desc:	read_descriptor
 * @actor:	read method
 *
 * This is a generic file read routine, and uses the
 * mapping->a_ops->readpage() function for the actual low-level stuff.
 *
 * This is really ugly. But the goto's actually try to clarify some
 * of the logic when it comes to error handling etc.
 *
 * Note the struct file* is only passed for the use of readpage.
 * It may be NULL.
 */


This is a generic file read routine, and uses the
mapping->a_ops->readpage() function for the actual low-level stuff.

This is really ugly. But the goto's actually try to clarify some
of the logic when it comes to error handling etc.

Note the struct file* is only passed for the use of readpage.
It may be NULL.
This is a generic file read routine and uses the mapping - > a_ops - > readpage () function for the actual low - level stuff . This is really ugly . But the goto ' s actually try to clarify some of the logic when it comes to error handling etc . Note the struct file * is only passed for the use of readpage . It may be NULL .
void do_generic_mapping_read(struct address_space *mapping,
			     struct file_ra_state *ra,
			     struct file *filp,
			     loff_t *ppos,
			     read_descriptor_t *desc,
			     read_actor_t actor)
{
	struct inode *inode = mapping->host;
	pgoff_t index;
	pgoff_t last_index;
	pgoff_t prev_index;
	unsigned long offset;      
	unsigned int prev_offset;
	int error;
	index = *ppos >> PAGE_CACHE_SHIFT;
	prev_index = ra->prev_pos >> PAGE_CACHE_SHIFT;
	prev_offset = ra->prev_pos & (PAGE_CACHE_SIZE-1);
	last_index = (*ppos + desc->count + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
	offset = *ppos & ~PAGE_CACHE_MASK;
	for (;;) {
		struct page *page;
		pgoff_t end_index;
		loff_t isize;
		unsigned long nr, ret;
		cond_resched();
find_page:
		page = find_get_page(mapping, index);
		if (!page) {
			page_cache_sync_readahead(mapping,
					ra, filp,
					index, last_index - index);
			page = find_get_page(mapping, index);
			if (unlikely(page == NULL))
				goto no_cached_page;
		}
		if (PageReadahead(page)) {
			page_cache_async_readahead(mapping,
					ra, filp, page,
					index, last_index - index);
		}
		if (!PageUptodate(page))
			goto page_not_up_to_date;
page_ok:
		isize = i_size_read(inode);
		end_index = (isize - 1) >> PAGE_CACHE_SHIFT;
		if (unlikely(!isize || index > end_index)) {
			page_cache_release(page);
			goto out;
		}
		nr = PAGE_CACHE_SIZE;
		if (index == end_index) {
			nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;
			if (nr <= offset) {
				page_cache_release(page);
				goto out;
			}
		}
		nr = nr - offset;
		if (mapping_writably_mapped(mapping))
			flush_dcache_page(page);
		if (prev_index != index || offset != prev_offset)
			mark_page_accessed(page);
		prev_index = index;
		ret = actor(desc, page, offset, nr);
		offset += ret;
		index += offset >> PAGE_CACHE_SHIFT;
		offset &= ~PAGE_CACHE_MASK;
		prev_offset = offset;
		page_cache_release(page);
		if (ret == nr && desc->count)
			continue;
		goto out;
page_not_up_to_date:
		lock_page(page);
		if (!page->mapping) {
			unlock_page(page);
			page_cache_release(page);
			continue;
		}
		if (PageUptodate(page)) {
			unlock_page(page);
			goto page_ok;
		}
readpage:
		error = mapping->a_ops->readpage(filp, page);
		if (unlikely(error)) {
			if (error == AOP_TRUNCATED_PAGE) {
				page_cache_release(page);
				goto find_page;
			}
			goto readpage_error;
		}
		if (!PageUptodate(page)) {
			lock_page(page);
			if (!PageUptodate(page)) {
				if (page->mapping == NULL) {
					unlock_page(page);
					page_cache_release(page);
					goto find_page;
				}
				unlock_page(page);
				error = -EIO;
				shrink_readahead_size_eio(filp, ra);
				goto readpage_error;
			}
			unlock_page(page);
		}
		goto page_ok;
readpage_error:
		desc->error = error;
		page_cache_release(page);
		goto out;
no_cached_page:
		page = page_cache_alloc_cold(mapping);
		if (!page) {
			desc->error = -ENOMEM;
			goto out;
		}
		error = add_to_page_cache_lru(page, mapping,
						index, GFP_KERNEL);
		if (error) {
			page_cache_release(page);
			if (error == -EEXIST)
				goto find_page;
			desc->error = error;
			goto out;
		}
		goto readpage;
	}
out:
	ra->prev_pos = prev_index;
	ra->prev_pos <<= PAGE_CACHE_SHIFT;
	ra->prev_pos |= prev_offset;
	*ppos = ((loff_t)index << PAGE_CACHE_SHIFT) + offset;
	if (filp)
		file_accessed(filp);
}
void do_generic_mapping_read ( struct address_space * mapping , struct file_ra_state * ra , struct file * filp , loff_t * ppos , read_descriptor_t * desc , read_actor_t actor ) { struct inode * inode = mapping -> host ; pgoff_t index ; pgoff_t last_index ; pgoff_t prev_index ; unsigned long offset ; unsigned int prev_offset ; int error ; index = * ppos >> PAGE_CACHE_SHIFT ; prev_index = ra -> prev_pos >> PAGE_CACHE_SHIFT ; prev_offset = ra -> prev_pos & ( PAGE_CACHE_SIZE - 1 ) ; last_index = ( * ppos + desc -> count + PAGE_CACHE_SIZE - 1 ) >> PAGE_CACHE_SHIFT ; offset = * ppos & ~ PAGE_CACHE_MASK ; for ( ; ; ) { struct page * page ; pgoff_t end_index ; loff_t isize ; unsigned long nr , ret ; cond_resched ( ) ; find_page : page = find_get_page ( mapping , index ) ; if ( ! page ) { page_cache_sync_readahead ( mapping , ra , filp , index , last_index - index ) ; page = find_get_page ( mapping , index ) ; if ( unlikely ( page == NULL ) ) goto no_cached_page ; } if ( PageReadahead ( page ) ) { page_cache_async_readahead ( mapping , ra , filp , page , index , last_index - index ) ; } if ( ! PageUptodate ( page ) ) goto page_not_up_to_date ; page_ok : isize = i_size_read ( inode ) ; end_index = ( isize - 1 ) >> PAGE_CACHE_SHIFT ; if ( unlikely ( ! isize || index > end_index ) ) { page_cache_release ( page ) ; goto out ; } nr = PAGE_CACHE_SIZE ; if ( index == end_index ) { nr = ( ( isize - 1 ) & ~ PAGE_CACHE_MASK ) + 1 ; if ( nr <= offset ) { page_cache_release ( page ) ; goto out ; } } nr = nr - offset ; if ( mapping_writably_mapped ( mapping ) ) flush_dcache_page ( page ) ; if ( prev_index != index || offset != prev_offset ) mark_page_accessed ( page ) ; prev_index = index ; ret = actor ( desc , page , offset , nr ) ; offset += ret ; index += offset >> PAGE_CACHE_SHIFT ; offset &= ~ PAGE_CACHE_MASK ; prev_offset = offset ; page_cache_release ( page ) ; if ( ret == nr && desc -> count ) continue ; goto out ; page_not_up_to_date : lock_page ( page ) ; if ( ! page -> mapping ) { unlock_page ( page ) ; page_cache_release ( page ) ; continue ; } if ( PageUptodate ( page ) ) { unlock_page ( page ) ; goto page_ok ; } readpage : error = mapping -> a_ops -> readpage ( filp , page ) ; if ( unlikely ( error ) ) { if ( error == AOP_TRUNCATED_PAGE ) { page_cache_release ( page ) ; goto find_page ; } goto readpage_error ; } if ( ! PageUptodate ( page ) ) { lock_page ( page ) ; if ( ! PageUptodate ( page ) ) { if ( page -> mapping == NULL ) { unlock_page ( page ) ; page_cache_release ( page ) ; goto find_page ; } unlock_page ( page ) ; error = - EIO ; shrink_readahead_size_eio ( filp , ra ) ; goto readpage_error ; } unlock_page ( page ) ; } goto page_ok ; readpage_error : desc -> error = error ; page_cache_release ( page ) ; goto out ; no_cached_page : page = page_cache_alloc_cold ( mapping ) ; if ( ! page ) { desc -> error = - ENOMEM ; goto out ; } error = add_to_page_cache_lru ( page , mapping , index , GFP_KERNEL ) ; if ( error ) { page_cache_release ( page ) ; if ( error == - EEXIST ) goto find_page ; desc -> error = error ; goto out ; } goto readpage ; } out : ra -> prev_pos = prev_index ; ra -> prev_pos <<= PAGE_CACHE_SHIFT ; ra -> prev_pos |= prev_offset ; * ppos = ( ( loff_t ) index << PAGE_CACHE_SHIFT ) + offset ; if ( filp ) file_accessed ( filp ) ; }
do_generic_mapping_read - generic file read routine
 @mapping:    address_space to be read
 @ra:         file's readahead state
 @filp:       the file to read
 @ppos:       current file position
 @desc:       read_descriptor
 @actor:      read method
do_generic_mapping_read - generic file read routine @mapping : address_space to be read @ra : file ' s readahead state @filp : the file to read @ppos : current file position @desc : read_descriptor @actor : read method
/* offset into pagecache page */ /*
		 * i_size must be checked after we know the page is Uptodate.
		 *
		 * Checking i_size after the check allows us to calculate
		 * the correct value for "nr", which means the zero-filled
		 * part of the page is not copied back to userspace (unless
		 * another truncate extends the file - this is desired though).
		 */ /* nr is the maximum number of bytes to copy from this page */ /* If users can be writing to this page using arbitrary
		 * virtual addresses, take care about potential aliasing
		 * before reading the page on the kernel side.
		 */ /*
		 * When a sequential read accesses a page several times,
		 * only mark it as accessed the first time.
		 */ /*
		 * Ok, we have the page, and it's up-to-date, so
		 * now we can copy it to user space...
		 *
		 * The actor routine returns how many bytes were actually used..
		 * NOTE! This may not be the same as how much of a user buffer
		 * we filled up (we may be padding etc), so we can only update
		 * "pos" here (the actor routine has to update the user buffer
		 * pointers and the remaining count).
		 */ /* Get exclusive access to the page ... */ /* Did it get truncated before we got the lock? */ /* Did somebody else fill it already? */ /* Start the actual read. The read will unlock the page. */ /*
					 * invalidate_inode_pages got it
					 */ /* UHHUH! A synchronous read error occurred. Report it */ /*
		 * Ok, it wasn't cached, so we need to create a new
		 * page..
		 */