分类: LINUX
2015-07-11 13:56:39
原文地址:block_prepare_write解析 作者:zixin
/*
*from为该页面起点,to为起点+写入长度
*get_block针对具体的文件系统把文件逻辑块号-〉设备上记录块
*/
int block_prepare_write(struct page *page, unsigned from, unsigned to,
get_block_t *get_block)
{
struct inode *inode = page->mapping->host;
int err = __block_prepare_write(inode, page, from, to, get_block);
if (err) {
ClearPageUptodate(page);
kunmap(page);
}
return err;
}
/*
*from为该页面起点,to为起点+写入长度
*/
static int __block_prepare_write(struct inode *inode, struct page *page,
unsigned from, unsigned to, get_block_t *get_block)
{
unsigned block_start, block_end;
unsigned long block;
int err = 0;
unsigned blocksize, bbits;
struct buffer_head *bh, *head, *wait[2], **wait_bh=wait;
char *kaddr = kmap(page);
/*块大小*/
blocksize = 1 << inode->i_blkbits;
/*
*调用create_empty_buffers为该页建立缓冲区队列,然后对队列进行初始化。
*没有涉及bh的state标志
*调用的create_buffers中设置bh->state = 0
*且把新分配的buffer放入BUF_CLEAN链表(BH_dirty标志为0)
* 缓冲队列头赋给page->buffer
*/
if (!page->buffers)
create_empty_buffers(page, inode->i_dev, blocksize);
head = page->buffers;
/*块位数*/
bbits = inode->i_blkbits;
/*当前页所在的块号*/
/*
*假设块大小为1k,则bbits = inode->i_blkbits, bbits为10
*则一页占2^(PAGE_CACHE_SHIFT-bbits)=2^(12-10)=2^2=4个块
*因此该页的逻辑起始块号为: page->index*每页块数
*即page->index*2^(PAGE_CACHE_SHIFT-bbits)=
* page->index<<(PAGE_CACHE_SHIFT-bbits)
*/
block = page->index << (PAGE_CACHE_SHIFT - bbits);
/*
*对页中每个buffer对应的bh和受写影响的每个bh
*block_start记录循环写入的总块大小
*/
for(bh = head, block_start = 0; bh != head || !block_start;
block++, block_start=block_end, bh = bh->b_this_page) {
if (!bh)
BUG();
/*累计写入块大小*/
block_end = block_start+blocksize;
if (block_end <= from)
continue;
if (block_start >= to)
break;
/*清BH_New标志(如果相应的文件块正好被分配但从未被访问)*/
clear_bit(BH_New, &bh->b_state);
/*
*检查如果BH_Mapped标志未设置
*调用get_block将文件逻辑块号-〉设备上记录块(相对磁盘分区的起始位置)
*块号存放与b_blocknr字段且设置BH_Mapped标志(完成映射)
*/
if (!buffer_mapped(bh)) {
err = get_block(inode, block, bh, 1);
if (err)
goto out;
/*
*检查BH_New标志,如果被设置
*调用unmap_underlying_metadata证实缓冲区高速缓存不包含引用磁盘同一块的
*一个脏缓冲区(可能性不大)
*如果不对整个缓冲区进行重写,则用0填充,跳过此buffer继续
*/
if (buffer_new(bh)) {
unmap_underlying_metadata(bh);
if (Page_Uptodate(page)) {
set_bit(BH_Uptodate, &bh->b_state);
continue;
}
if (block_end > to)
memset(kaddr+to, 0, block_end-to);
if (block_start < from)
memset(kaddr+block_start, 0, from-block_start);
if (block_end > to || block_start < from)
flush_dcache_page(page);
continue;
}
}//buffer_mapped(bh)——if
/*
*如果page的读操作完成的PG_uptodate标志设置了,则将其缓冲区的BH_Uptodate也设置
*/
if (Page_Uptodate(page)) {
set_bit(BH_Uptodate, &bh->b_state);
continue;
}
/*
*检如果不对整个缓冲区进行重写,检查其BH_Uptodate标志,未被设置(不一致)
*调用ll_rw_block来从磁盘读入块的内容
*ll_rw_block函数中自定义了I/O完后调用了end_buffer_io_sync
*/
if (!buffer_uptodate(bh) &&
(block_start < from || block_end > to)) {
ll_rw_block(READ, 1, &bh);
*wait_bh++=bh;
}
} // for循环
/*
* If we issued read requests - let them complete.
*/
/*
* 阻塞当前进程,直到for循环中的ll_rw_block读操作全部完成
*/
while(wait_bh > wait) {
wait_on_buffer(*--wait_bh);
if (!buffer_uptodate(*wait_bh))
return -EIO;
}
return 0;
/*检查未映射的时候调用get_block出错跳转的位置*/
out:
/*
* Zero out any newly allocated blocks to avoid exposing stale
* data. If BH_New is set, we know that the block was newly
* allocated in the above loop.
*
* Details the buffer can be new and uptodate because:
* 1) hole in uptodate page, get_block(create) allocate the block,
* so the buffer is new and additionally we also mark it uptodate
* 2) The buffer is not mapped and uptodate due a previous partial read.
*
* We can always ignore uptodate buffers here, if you mark a buffer
* uptodate you must make sure it contains the right data first.
*
* We must stop the "undo/clear" fixup pass not at the caller "to"
* but at the last block that we successfully arrived in the main loop.
*/
bh = head;
to = block_start; /* stop at the last successfully handled block */
block_start = 0;
do {
block_end = block_start+blocksize;
if (block_end <= from)
goto next_bh;
if (block_start >= to)
break;
if (buffer_new(bh) && !buffer_uptodate(bh)) {
memset(kaddr+block_start, 0, bh->b_size);
flush_dcache_page(page);
set_bit(BH_Uptodate, &bh->b_state);
mark_buffer_dirty(bh);
}
next_bh:
block_start = block_end;
bh = bh->b_this_page;
} while (bh != head);
return err;
}