linux/mmnommu/filemap.c
/*
 * This is a generic file read routine, and uses the
 * inode->i_op->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.
 */
This is a generic file read routine, and uses the
inode->i_op->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.
This is a generic file read routine and uses the inode - > i_op - > 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 .
void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc, read_actor_t actor)
{
	struct inode *inode = filp->f_dentry->d_inode;
	struct address_space *mapping = inode->i_mapping;
	unsigned long index, offset;
	struct page *cached_page;
	int reada_ok;
	int error;
	int max_readahead = get_max_readahead(inode);
	cached_page = NULL;
	index = *ppos >> PAGE_CACHE_SHIFT;
	offset = *ppos & ~PAGE_CACHE_MASK;
	if (index > filp->f_raend || index + filp->f_rawin < filp->f_raend) {
		reada_ok = 0;
		filp->f_raend = 0;
		filp->f_ralen = 0;
		filp->f_ramax = 0;
		filp->f_rawin = 0;
	} else {
		reada_ok = 1;
	}
	if (!index && offset + desc->count <= (PAGE_CACHE_SIZE >> 1)) {
		filp->f_ramax = 0;
	} else {
		unsigned long needed;
		needed = ((offset + desc->count) >> PAGE_CACHE_SHIFT) + 1;
		if (filp->f_ramax < needed)
			filp->f_ramax = needed;
		if (reada_ok && filp->f_ramax < MIN_READAHEAD)
				filp->f_ramax = MIN_READAHEAD;
		if (filp->f_ramax > max_readahead)
			filp->f_ramax = max_readahead;
	}
	for (;;) {
		struct page *page, **hash;
		unsigned long end_index, nr;
		end_index = inode->i_size >> PAGE_CACHE_SHIFT;
		if (index > end_index)
			break;
		nr = PAGE_CACHE_SIZE;
		if (index == end_index) {
			nr = inode->i_size & ~PAGE_CACHE_MASK;
			if (nr <= offset)
				break;
		}
		nr = nr - offset;
		hash = page_hash(mapping, index);
		spin_lock(&pagecache_lock);
		page = __find_page_nolock(mapping, index, *hash);
		if (!page)
			goto no_cached_page;
found_page:
		page_cache_get(page);
		spin_unlock(&pagecache_lock);
		if (!Page_Uptodate(page))
			goto page_not_up_to_date;
		generic_file_readahead(reada_ok, filp, inode, page);
page_ok:
		if (mapping->i_mmap_shared != NULL)
			flush_dcache_page(page);
		nr = actor(desc, page, offset, nr);
		offset += nr;
		index += offset >> PAGE_CACHE_SHIFT;
		offset &= ~PAGE_CACHE_MASK;
		page_cache_release(page);
		if (nr && desc->count)
			continue;
		break;
page_not_up_to_date:
		generic_file_readahead(reada_ok, filp, inode, page);
		if (Page_Uptodate(page))
			goto page_ok;
		lock_page(page);
		if (!page->mapping) {
			UnlockPage(page);
			page_cache_release(page);
			continue;
		}
		if (Page_Uptodate(page)) {
			UnlockPage(page);
			goto page_ok;
		}
readpage:
		error = mapping->a_ops->readpage(filp, page);
		if (!error) {
			if (Page_Uptodate(page))
				goto page_ok;
			generic_file_readahead(reada_ok, filp, inode, page);
			wait_on_page(page);
			if (Page_Uptodate(page))
				goto page_ok;
			error = -EIO;
		}
		desc->error = error;
		page_cache_release(page);
		break;
no_cached_page:
		if (!cached_page) {
			spin_unlock(&pagecache_lock);
			cached_page = page_cache_alloc();
			if (!cached_page) {
				desc->error = -ENOMEM;
				break;
			}
			spin_lock(&pagecache_lock);
			page = __find_page_nolock(mapping, index, *hash);
			if (page)
				goto found_page;
		}
		page = cached_page;
		__add_to_page_cache(page, mapping, index, hash);
		spin_unlock(&pagecache_lock);
		cached_page = NULL;
		goto readpage;
	}
	*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
	filp->f_reada = 1;
	if (cached_page)
		page_cache_free(cached_page);
	UPDATE_ATIME(inode);
}
void do_generic_file_read ( struct file * filp , loff_t * ppos , read_descriptor_t * desc , read_actor_t actor ) { struct inode * inode = filp -> f_dentry -> d_inode ; struct address_space * mapping = inode -> i_mapping ; unsigned long index , offset ; struct page * cached_page ; int reada_ok ; int error ; int max_readahead = get_max_readahead ( inode ) ; cached_page = NULL ; index = * ppos >> PAGE_CACHE_SHIFT ; offset = * ppos & ~ PAGE_CACHE_MASK ; if ( index > filp -> f_raend || index + filp -> f_rawin < filp -> f_raend ) { reada_ok = 0 ; filp -> f_raend = 0 ; filp -> f_ralen = 0 ; filp -> f_ramax = 0 ; filp -> f_rawin = 0 ; } else { reada_ok = 1 ; } if ( ! index && offset + desc -> count <= ( PAGE_CACHE_SIZE >> 1 ) ) { filp -> f_ramax = 0 ; } else { unsigned long needed ; needed = ( ( offset + desc -> count ) >> PAGE_CACHE_SHIFT ) + 1 ; if ( filp -> f_ramax < needed ) filp -> f_ramax = needed ; if ( reada_ok && filp -> f_ramax < MIN_READAHEAD ) filp -> f_ramax = MIN_READAHEAD ; if ( filp -> f_ramax > max_readahead ) filp -> f_ramax = max_readahead ; } for ( ; ; ) { struct page * page , * * hash ; unsigned long end_index , nr ; end_index = inode -> i_size >> PAGE_CACHE_SHIFT ; if ( index > end_index ) break ; nr = PAGE_CACHE_SIZE ; if ( index == end_index ) { nr = inode -> i_size & ~ PAGE_CACHE_MASK ; if ( nr <= offset ) break ; } nr = nr - offset ; hash = page_hash ( mapping , index ) ; spin_lock ( & pagecache_lock ) ; page = __find_page_nolock ( mapping , index , * hash ) ; if ( ! page ) goto no_cached_page ; found_page : page_cache_get ( page ) ; spin_unlock ( & pagecache_lock ) ; if ( ! Page_Uptodate ( page ) ) goto page_not_up_to_date ; generic_file_readahead ( reada_ok , filp , inode , page ) ; page_ok : if ( mapping -> i_mmap_shared != NULL ) flush_dcache_page ( page ) ; nr = actor ( desc , page , offset , nr ) ; offset += nr ; index += offset >> PAGE_CACHE_SHIFT ; offset &= ~ PAGE_CACHE_MASK ; page_cache_release ( page ) ; if ( nr && desc -> count ) continue ; break ; page_not_up_to_date : generic_file_readahead ( reada_ok , filp , inode , page ) ; if ( Page_Uptodate ( page ) ) goto page_ok ; lock_page ( page ) ; if ( ! page -> mapping ) { UnlockPage ( page ) ; page_cache_release ( page ) ; continue ; } if ( Page_Uptodate ( page ) ) { UnlockPage ( page ) ; goto page_ok ; } readpage : error = mapping -> a_ops -> readpage ( filp , page ) ; if ( ! error ) { if ( Page_Uptodate ( page ) ) goto page_ok ; generic_file_readahead ( reada_ok , filp , inode , page ) ; wait_on_page ( page ) ; if ( Page_Uptodate ( page ) ) goto page_ok ; error = - EIO ; } desc -> error = error ; page_cache_release ( page ) ; break ; no_cached_page : if ( ! cached_page ) { spin_unlock ( & pagecache_lock ) ; cached_page = page_cache_alloc ( ) ; if ( ! cached_page ) { desc -> error = - ENOMEM ; break ; } spin_lock ( & pagecache_lock ) ; page = __find_page_nolock ( mapping , index , * hash ) ; if ( page ) goto found_page ; } page = cached_page ; __add_to_page_cache ( page , mapping , index , hash ) ; spin_unlock ( & pagecache_lock ) ; cached_page = NULL ; goto readpage ; } * ppos = ( ( loff_t ) index << PAGE_CACHE_SHIFT ) + offset ; filp -> f_reada = 1 ; if ( cached_page ) page_cache_free ( cached_page ) ; UPDATE_ATIME ( inode ) ; }
This is a generic file read routine, and uses the
 inode->i_op->readpage() function for the actual low-level
 stuff.
This is a generic file read routine and uses the inode - > i_op - > readpage () function for the actual low - level stuff .
/*
 * If the current position is outside the previous read-ahead window, 
 * we reset the current read-ahead context and set read ahead max to zero
 * (will be set to just needed value later),
 * otherwise, we assume that the file accesses are sequential enough to
 * continue read-ahead.
 */ /*
 * Adjust the current value of read-ahead max.
 * If the read operation stay in the first half page, force no readahead.
 * Otherwise try to increase read ahead max just enough to do the read request.
 * Then, at least MIN_READAHEAD if read ahead is ok,
 * and at most MAX_READAHEAD in all cases.
 */ /*
		 * Try to find the data in the page cache..
		 */ /* 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.
		 */ /*
		 * 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).
		 */ /*
 * Ok, the page was not immediately readable, so let's try to read ahead while we're at it..
 */ /* Get exclusive access to the page ... */ /* Did it get unhashed before we got the lock? */ /* Did somebody else fill it already? */ /* ... and start the actual read. The read will unlock the page. */ /* Again, try some read-ahead while waiting for the page to finish.. */ /* UHHUH! A synchronous read error occurred. Report it */ /*
		 * Ok, it wasn't cached, so we need to create a new
		 * page..
		 *
		 * We get here with the page cache lock held.
		 */ /*
			 * Somebody may have added the page while we
			 * dropped the page cache lock. Check for that.
			 */ /*
		 * Ok, add the new page to the hash-queues...
		 */