/*
* block_write_begin takes care of the basic task of block allocation and
* bringing partial write blocks uptodate first.
*
* If *pagep is not NULL, then block_write_begin uses the locked page
* at *pagep rather than allocating its own. In this case, the page will
* not be unlocked or deallocated on failure.
*/
int block_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata,
get_block_t *get_block)
{
struct inode *inode = mapping->host;
int status = 0;
struct page *page;
pgoff_t index;
unsigned start, end;
int ownpage = 0;
index = pos >> PAGE_CACHE_SHIFT;
start = pos & (PAGE_CACHE_SIZE - 1);
end = start + len;
page = *pagep;
if (page == NULL)
{
ownpage = 1;
/*
查找page或者,创建page
*/
page = grab_cache_page_write_begin(mapping, index, flags);
if (!page)
{
status = -ENOMEM;
goto out;
}
*pagep = page;
}
else
BUG_ON(!PageLocked(page));
/*
对page缓冲区做相应的操作
*/
status = __block_prepare_write(inode, page, start, end, get_block);
if (unlikely(status))
{
ClearPageUptodate(page);
if (ownpage)
{
unlock_page(page);
page_cache_release(page);
*pagep = NULL;
/*
* prepare_write() may have instantiated a few blocks
* outside i_size. Trim these off again. Don't need
* i_size_read because we hold i_mutex.
*/
if (pos + len > inode->i_size)
vmtruncate(inode, inode->i_size);
}
}
out:
return status;
}
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;
sector_t block;
int err = 0;
unsigned blocksize, bbits;
struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
BUG_ON(!PageLocked(page));
BUG_ON(from > PAGE_CACHE_SIZE);
BUG_ON(to > PAGE_CACHE_SIZE);
BUG_ON(from > to);
blocksize = 1 << inode->i_blkbits;
/*这个位置首先检查,page是否带有缓冲区,如果没有创建page的缓冲区头*/
if (!page_has_buffers(page))
create_empty_buffers(page, blocksize, 0);
head = page_buffers(page);
bbits = inode->i_blkbits;
block = (sector_t)page->index << (PAGE_CACHE_SHIFT - bbits);
/*对该page的所有块逐一操作*/
for(bh = head, block_start = 0; bh != head || !block_start;
block++, block_start = block_end, bh = bh->b_this_page)
{
block_end = block_start + blocksize;
/*如果这个block,不在写入的范围内,无需进行多余的操作
只需要判断需不需要设置uptodate标记*/
if (block_end <= from || block_start >= to)
{
if (PageUptodate(page))
{
if (!buffer_uptodate(bh))
set_buffer_uptodate(bh);
}
continue;
}
/*清除掉new标记*/
if (buffer_new(bh))
clear_buffer_new(bh);
/*如果缓冲区还没有mapped,就调用get_block函数给相应的缓冲区分配block
判断发现缓冲区是新分配的,需要查找这个缓冲区是不是还有未写入的缓冲,
需要先将这个未写入的缓冲同步写入才行,避免出现混乱
*/
if (!buffer_mapped(bh))
{
WARN_ON(bh->b_size != blocksize);
err = get_block(inode, block, bh, 1);
if (err)
break;
if (buffer_new(bh))
{
unmap_underlying_metadata(bh->b_bdev,
bh->b_blocknr);
if (PageUptodate(page))
{
clear_buffer_new(bh);
set_buffer_uptodate(bh);
mark_buffer_dirty(bh);
continue;
}
if (block_end > to || block_start < from)
zero_user_segments(page,
to, block_end,
block_start, from);
continue;
}
}
/*page已经是最新数据,page中的内容和磁盘上的一致
每个缓冲区也可以设置uptodata标记*/
if (PageUptodate(page))
{
if (!buffer_uptodate(bh))
set_buffer_uptodate(bh);
continue;
}
/*缓冲区的内容不是最新的,和磁盘不同步,这个缓冲区上没有读操作
并且缓冲区在本次写操作的范围里,执行读操作,
这样做的目的是为了,由于这个缓冲区数据不是最新的,如果在写操作
过程中并非出现读,就会造成磁盘操作造成干扰*/
if (!buffer_uptodate(bh) && !buffer_delay(bh) &&
!buffer_unwritten(bh) &&
(block_start < from || block_end > to))
{
ll_rw_block(READ, 1, &bh);
*wait_bh++ = bh;
}
}
/*
* If we issued read requests - let them complete.
*/
/*如果上面有同步读操作,等待读操作完成*/
while(wait_bh > wait)
{
wait_on_buffer(*--wait_bh);
if (!buffer_uptodate(*wait_bh))
err = -EIO;
}
if (unlikely(err))
page_zero_new_buffers(page, from, to);
return err;
}
|