static struct bio *
do_mpage_readpage( struct bio *bio, struct page *page, unsigned nr_pages,
sector_t *last_block_in_bio, struct buffer_head *map_bh,
unsigned long *first_logical_block, get_block_t get_block)
{
struct inode *inode = page->mapping->host;
const unsigned blkbits = inode->i_blkbits;
const unsigned blocks_per_page = PAGE_CACHE_SIZE >> blkbits;
const unsigned blocksize = 1 << blkbits;
sector_t block_in_file;
sector_t last_block;
sector_t last_block_in_file;
sector_t blocks[MAX_BUF_PER_PAGE];
unsigned page_block;
unsigned first_hole = blocks_per_page;
struct block_device *bdev = NULL;
int length;
int fully_mapped = 1 ;
unsigned nblocks;
unsigned relative_block;
if (page_has_buffers(page))
goto confused;
block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits);
last_block = block_in_file + nr_pages *blocks_per_page;
last_block_in_file = (i_size_read(inode) + blocksize - 1 ) >> blkbits;
if (last_block > last_block_in_file)
last_block = last_block_in_file;
page_block = 0 ;
nblocks = map_bh->b_size >> blkbits;
if (buffer_mapped(map_bh) && block_in_file > *first_logical_block &&
block_in_file < (*first_logical_block + nblocks))
{
unsigned map_offset = block_in_file - *first_logical_block;
unsigned last = nblocks - map_offset;
for (relative_block = 0 ; ; relative_block++)
{
if (relative_block == last)
{
clear_buffer_mapped(map_bh);
break ;
}
if (page_block == blocks_per_page)
break ;
blocks[page_block] = map_bh->b_blocknr + map_offset +
relative_block;
page_block++;
block_in_file++;
}
bdev = map_bh->b_bdev;
}
map_bh->b_page = page;
while (page_block < blocks_per_page)
{
map_bh->b_state = 0 ;
map_bh->b_size = 0 ;
if (block_in_file < last_block)
{
map_bh->b_size = (last_block - block_in_file) << blkbits;
if (get_block(inode, block_in_file, map_bh, 0 ))
goto confused;
*first_logical_block = block_in_file;
}
if (!buffer_mapped(map_bh))
{
fully_mapped = 0 ;
if (first_hole == blocks_per_page)
first_hole = page_block;
page_block++;
block_in_file++;
continue ;
}
if (buffer_uptodate(map_bh))
{
map_buffer_to_page(page, map_bh, page_block);
goto confused;
}
if (first_hole != blocks_per_page)
goto confused;
if (page_block && blocks[page_block- 1 ] != map_bh->b_blocknr - 1 )
goto confused;
nblocks = map_bh->b_size >> blkbits;
for (relative_block = 0 ; ; relative_block++)
{
if (relative_block == nblocks)
{
clear_buffer_mapped(map_bh);
break ;
}
else if (page_block == blocks_per_page)
break ;
blocks[page_block] = map_bh->b_blocknr + relative_block;
page_block++;
block_in_file++;
}
bdev = map_bh->b_bdev;
}
if (first_hole != blocks_per_page)
{
zero_user_segment(page, first_hole << blkbits, PAGE_CACHE_SIZE);
if (first_hole == 0 )
{
SetPageUptodate(page);
unlock_page(page);
goto out;
}
}
else if (fully_mapped)
{
SetPageMappedToDisk(page);
}
if (bio && (*last_block_in_bio != blocks[ 0 ] - 1 ))
bio = mpage_bio_submit(READ, bio);
alloc_new:
if (bio == NULL)
{
bio = mpage_alloc(bdev, blocks[ 0 ] << (blkbits - 9 ),
min_t( int , nr_pages, bio_get_nr_vecs(bdev)),
GFP_KERNEL);
if (bio == NULL)
goto confused;
}
length = first_hole << blkbits;
if (bio_add_page(bio, page, length, 0 ) < length)
{
bio = mpage_bio_submit(READ, bio);
goto alloc_new;
}
relative_block = block_in_file - *first_logical_block;
nblocks = map_bh->b_size >> blkbits;
if ((buffer_boundary(map_bh) && relative_block == nblocks) ||
(first_hole != blocks_per_page))
bio = mpage_bio_submit(READ, bio);
else
*last_block_in_bio = blocks[blocks_per_page - 1 ];
out:
return bio;
confused:
if (bio)
bio = mpage_bio_submit(READ, bio);
if (!PageUptodate(page))
block_read_full_page(page, get_block);
else
unlock_page(page);
goto out;
}