/*
* This is the worker routine which does all the work of mapping the disk
* blocks and constructs largest possible bios, submits them for IO if the
* blocks are not contiguous on the disk.
*
* We pass a buffer_head back and forth and use its buffer_mapped() flag to
* represent the validity of its disk mapping and to decide when to do the next
* get_block() call.
*/
/*这个函数试图读取文件中的一个page大小的数据,最理想的情况下就是这个page大小
的数据都是在连续的物理磁盘上面的,然后函数只需要提交一个bio请求就可以获取
所有的数据,这个函数大部分工作在检查page上所有的物理块是否连续,检查的方法
就是调用文件系统提供的get_block函数,如果不连续,需要调用block_read_full_page
函数采用buffer 缓冲区的形式来逐个块获取数据*/
/*
1、调用get_block函数检查page中是不是所有的物理块都连续
2、如果连续调用mpage_bio_submit函数请求整个page的数据
3、如果不连续调用block_read_full_page逐个block读取
*/
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 本page中的第一个block number
last_block 本page中最后一个block 的大小
last_block_in_file 文件大小求出文件的最后一个block 大小*/
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;
/*last_block 最后等于本次对这个page操作的最后一个block大小*/
if (last_block > last_block_in_file)
last_block = last_block_in_file;
page_block = 0 ;
/*
* Map blocks using the result from the previous get_blocks call first.
*/
nblocks = map_bh->b_size >> blkbits;
/*对于普通情况mpage_readpage调用下,map_bh只是一个临时变量不会走到
下面的分支*/
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;
}
/*
* Then do more get_blocks calls until we are done with this page.
*/
map_bh->b_page = page;
/*这个循环是比较关键的路径,理解这个函数至关重要
1、page_block从0开始循环,它表示在这个page内的block大小
2、调用get_block 函数查找对应逻辑块的物理块号是多少
3、如果遇到了文件空洞、page上的物理块不连续就会跳转到confused
4、将这个page中每个逻辑块对应的物理块都保存到临时的数组blocks[] 中*/
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 ;
}
/* some filesystems will copy data into the page during
* the get_block call, in which case we don't want to
* read it again. map_buffer_to_page copies the data
* we just collected from get_block into the page's buffers
* so readpage doesn't have to repeat the get_block call
*/
if (buffer_uptodate(map_bh))
{
map_buffer_to_page(page, map_bh, page_block);
goto confused;
}
if (first_hole != blocks_per_page)
goto confused; /* hole -> non-hole */
/* Contiguous blocks? */
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;
}
/*如果发现文件中有洞,将整个page清0,因为文件洞的区域
物理层不会真的去磁盘上读取,必须在这里主动清零,否则
文件洞区域内容可能随机*/
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);
}
/*
* This page will go to BIO. Do we need to send this BIO off first?
*/
/*bio 为NULL,目前分析的场景可以跳过去*/
if (bio && (*last_block_in_bio != blocks[ 0 ] - 1 ))
bio = mpage_bio_submit(READ, bio);
alloc_new:
if (bio == NULL)
{
/*重新分配一个bio结构体
blocks[0] << (blkbits - 9) 这个是page中第一个逻辑块的物理块号,
转换成物理扇区号*/
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:
/*一切顺利,整个page中的物理块是相连的,返回一个bio*/
return bio;
confused:
if (bio)
bio = mpage_bio_submit(READ, bio);
/*page 中的物理块不相连,没有办法一个一个buffer去读取出来
*/
if (!PageUptodate(page))
block_read_full_page(page, get_block);
else
unlock_page(page);
goto out;
}