f2fs: refactor ->page_mkwrite() flow
Thread A Thread B - f2fs_vm_page_mkwrite - f2fs_setattr - down_write(i_mmap_sem) - truncate_setsize - f2fs_truncate - up_write(i_mmap_sem) - f2fs_reserve_block reserve NEW_ADDR - skip dirty page due to truncation 1. we don't need to rserve new block address for a truncated page. 2. dn.data_blkaddr is used out of node page lock coverage. Refactor ->page_mkwrite() flow to fix above issues: - use __do_map_lock() to avoid racing checkpoint() - lock data page in prior to dnode page - cover f2fs_reserve_block with i_mmap_sem lock - wait page writeback before zeroing page Signed-off-by: Chao Yu <yuchao0@huawei.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
bab475c541
commit
39a8695824
|
@ -972,7 +972,7 @@ map_blocks:
|
|||
return err;
|
||||
}
|
||||
|
||||
static inline void __do_map_lock(struct f2fs_sb_info *sbi, int flag, bool lock)
|
||||
void __do_map_lock(struct f2fs_sb_info *sbi, int flag, bool lock)
|
||||
{
|
||||
if (flag == F2FS_GET_BLOCK_PRE_AIO) {
|
||||
if (lock)
|
||||
|
|
|
@ -3055,6 +3055,7 @@ struct page *f2fs_get_lock_data_page(struct inode *inode, pgoff_t index,
|
|||
struct page *f2fs_get_new_data_page(struct inode *inode,
|
||||
struct page *ipage, pgoff_t index, bool new_i_size);
|
||||
int f2fs_do_write_data_page(struct f2fs_io_info *fio);
|
||||
void __do_map_lock(struct f2fs_sb_info *sbi, int flag, bool lock);
|
||||
int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
|
||||
int create, int flag);
|
||||
int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
|
|
|
@ -47,7 +47,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
|||
struct page *page = vmf->page;
|
||||
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct dnode_of_data dn;
|
||||
struct dnode_of_data dn = { .node_changed = false };
|
||||
int err;
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
|
@ -59,19 +59,6 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
|||
|
||||
f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
|
||||
|
||||
/* block allocation */
|
||||
f2fs_lock_op(sbi);
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = f2fs_reserve_block(&dn, page->index);
|
||||
if (err) {
|
||||
f2fs_unlock_op(sbi);
|
||||
goto out;
|
||||
}
|
||||
f2fs_put_dnode(&dn);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
|
||||
file_update_time(vmf->vma->vm_file);
|
||||
down_read(&F2FS_I(inode)->i_mmap_sem);
|
||||
lock_page(page);
|
||||
|
@ -83,11 +70,28 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
|||
goto out_sem;
|
||||
}
|
||||
|
||||
/* block allocation */
|
||||
__do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, true);
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = f2fs_get_block(&dn, page->index);
|
||||
f2fs_put_dnode(&dn);
|
||||
__do_map_lock(sbi, F2FS_GET_BLOCK_PRE_AIO, false);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
goto out_sem;
|
||||
}
|
||||
|
||||
/* fill the page */
|
||||
f2fs_wait_on_page_writeback(page, DATA, false);
|
||||
|
||||
/* wait for GCed page writeback via META_MAPPING */
|
||||
f2fs_wait_on_block_writeback(inode, dn.data_blkaddr);
|
||||
|
||||
/*
|
||||
* check to see if the page is mapped already (no holes)
|
||||
*/
|
||||
if (PageMappedToDisk(page))
|
||||
goto mapped;
|
||||
goto out_sem;
|
||||
|
||||
/* page is wholly or partially inside EOF */
|
||||
if (((loff_t)(page->index + 1) << PAGE_SHIFT) >
|
||||
|
@ -104,16 +108,11 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
|||
f2fs_update_iostat(sbi, APP_MAPPED_IO, F2FS_BLKSIZE);
|
||||
|
||||
trace_f2fs_vm_page_mkwrite(page, DATA);
|
||||
mapped:
|
||||
/* fill the page */
|
||||
f2fs_wait_on_page_writeback(page, DATA, false);
|
||||
|
||||
/* wait for GCed page writeback via META_MAPPING */
|
||||
f2fs_wait_on_block_writeback(inode, dn.data_blkaddr);
|
||||
|
||||
out_sem:
|
||||
up_read(&F2FS_I(inode)->i_mmap_sem);
|
||||
out:
|
||||
|
||||
f2fs_balance_fs(sbi, dn.node_changed);
|
||||
|
||||
sb_end_pagefault(inode->i_sb);
|
||||
f2fs_update_time(sbi, REQ_TIME);
|
||||
err:
|
||||
|
|
Loading…
Reference in New Issue