/*
* 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.
*/
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;
//将块的位数赋给blkbits
const unsigned blkbits = inode->i_blkbits;
//计算一个页面中的数据块数目
const unsigned blocks_per_page = PAGE_CACHE_SIZE >> blkbits;
//计算block 的大小
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;
//如果是一个缓存区页(PG_private被置位),跳转到confused
//据目前的理解,PG_private置位意味着page中的缓冲区段在磁盘上不连续
if (page_has_buffers(page))
goto confused;
//通过页的索引获得相对于文件头的首个块号
block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits);
//计算要传输的最后一个块号,这里nr_pages是1,即该页中的最后一块
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;
/*
* Map blocks using the result from the previous get_blocks call first.
*/
//nblock使用来干什么的啊?是指一个buffer_head所能指向的块数吗?
//这么说来,buffer_head中的b_size所指的块大小和inode中的块大小不一样?
nblocks = map_bh->b_size >> blkbits;
//如果该缓冲区首部已经映射到磁盘(b_bdev、b_blocknr已经赋值),且……
//注:首次传入的map_bh是经过clear_buffer_mapped()处理过的
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;
//page_block初值为0,block_per_page是一页中的块数,即循环次数为页中块数,即对页中所有块进行处理
//即:一次调用get_block()获得的连续的磁盘的块数不够一页的话,就反复调用
while (page_block < blocks_per_page) {
map_bh->b_state = 0;
map_bh->b_size = 0;
if (block_in_file < last_block) {
//设置buffer_head中的size,这个buffer_head指向连续的块的块数,是指期望获得的最大连续块数?
//每次计算这个,是因为要反复使用这个结构来获取b_blocknr字段
map_bh->b_size = (last_block-block_in_file) << blkbits;
//get_block()函数将会buffer_head的映射和size的赋值
if (get_block(inode, block_in_file, map_bh, 0))
goto confused;
*first_logical_block = block_in_file;
}
//??map_bh没有映射,这应该就对应文件洞了
if (!buffer_mapped(map_bh)) {
//设置页全部映射到磁盘的标志为0
fully_mapped = 0;
//将文件洞记录下来
if (first_hole == blocks_per_page)
first_hole = page_block;
page_block++;
block_in_file++;
clear_buffer_mapped(map_bh);
//继续处理下一个块
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;
}
//走到这步来了说明遇到了一个文件洞,但是之后的块又映射了,这时应该将遇到洞以前的块处理掉,故跳至confused
if (first_hole != blocks_per_page)
goto confused; /* hole -> non-hole */
/* Contiguous blocks? */
//通过比较blocks数组中的上一个元素中的值和这次获得的(b_blocknr-1)比较,判断是否连续
if (page_block && blocks[page_block-1] != map_bh->b_blocknr-1)
goto confused;
//计算出这个buffer_head指向的连续的块的块数
nblocks = map_bh->b_size >> blkbits;
//这个循环用来获取 块在磁盘上连续的页 中所有块 在磁盘中的块号
//循环的结束条件是buffer_head所指向的块数减一至0 或 page中所有块在磁盘中的块号都以得出
for (relative_block = 0; ; relative_block++) {
if (relative_block == nblocks) {
clear_buffer_mapped(map_bh);
break;
} else if (page_block == blocks_per_page)
break;
//为page中连续的块计算在磁盘中的编号,放入blocks数组中
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) {
char *kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr + (first_hole << blkbits), 0,
PAGE_CACHE_SIZE - (first_hole << blkbits));
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
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
//如果bio不为空,而其中最后一个block与现在的不连续,则提交以前的bio
if (bio && (*last_block_in_bio != blocks[0] - 1))
bio = mpage_bio_submit(READ, bio);
alloc_new:
if (bio == NULL) {
//通过mpage_alloc创建新的bio
// 2**9即512,blocks[0] << (blkbits - 9)得出首个扇区号
//min_t(int, nr_pages, bio_get_nr_vecs(bdev))即,这个bio中的iovec个数
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;
}
if (buffer_boundary(map_bh) || (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;
}
2010-3-25 do_mpage_readpage 代码注释
最新推荐文章于 2023-11-29 15:10:05 发布