f2fs: fix race on allocating and deallocating a dentry block
There are two threads: f2fs_delete_entry() get_new_data_page() f2fs_reserve_block() dn.blkaddr = XXX lock_page(dentry_block) truncate_hole() dn.blkaddr = NULL unlock_page(dentry_block) lock_page(dentry_block) fill the block from XXX address add new dentries unlock_page(dentry_block) Later, f2fs_write_data_page() will truncate the dentry_block, since its block address is NULL. The reason for this was due to the wrong lock order. In this case, we should do f2fs_reserve_block() after locking its dentry block. Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
eaa693f4dc
commit
01f28610a1
|
@ -1076,20 +1076,22 @@ struct page *get_new_data_page(struct inode *inode,
|
||||||
struct page *page;
|
struct page *page;
|
||||||
struct dnode_of_data dn;
|
struct dnode_of_data dn;
|
||||||
int err;
|
int err;
|
||||||
|
repeat:
|
||||||
|
page = grab_cache_page(mapping, index);
|
||||||
|
if (!page)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
set_new_dnode(&dn, inode, ipage, NULL, 0);
|
set_new_dnode(&dn, inode, ipage, NULL, 0);
|
||||||
err = f2fs_reserve_block(&dn, index);
|
err = f2fs_reserve_block(&dn, index);
|
||||||
if (err)
|
if (err) {
|
||||||
|
f2fs_put_page(page, 1);
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
repeat:
|
|
||||||
page = grab_cache_page(mapping, index);
|
|
||||||
if (!page) {
|
|
||||||
err = -ENOMEM;
|
|
||||||
goto put_err;
|
|
||||||
}
|
}
|
||||||
|
if (!ipage)
|
||||||
|
f2fs_put_dnode(&dn);
|
||||||
|
|
||||||
if (PageUptodate(page))
|
if (PageUptodate(page))
|
||||||
return page;
|
goto got_it;
|
||||||
|
|
||||||
if (dn.data_blkaddr == NEW_ADDR) {
|
if (dn.data_blkaddr == NEW_ADDR) {
|
||||||
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
|
||||||
|
@ -1104,20 +1106,19 @@ struct page *get_new_data_page(struct inode *inode,
|
||||||
};
|
};
|
||||||
err = f2fs_submit_page_bio(&fio);
|
err = f2fs_submit_page_bio(&fio);
|
||||||
if (err)
|
if (err)
|
||||||
goto put_err;
|
return ERR_PTR(err);
|
||||||
|
|
||||||
lock_page(page);
|
lock_page(page);
|
||||||
if (unlikely(!PageUptodate(page))) {
|
if (unlikely(!PageUptodate(page))) {
|
||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
err = -EIO;
|
return ERR_PTR(-EIO);
|
||||||
goto put_err;
|
|
||||||
}
|
}
|
||||||
if (unlikely(page->mapping != mapping)) {
|
if (unlikely(page->mapping != mapping)) {
|
||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
goto repeat;
|
goto repeat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
got_it:
|
||||||
if (new_i_size &&
|
if (new_i_size &&
|
||||||
i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
|
i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) {
|
||||||
i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT));
|
i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT));
|
||||||
|
@ -1125,10 +1126,6 @@ struct page *get_new_data_page(struct inode *inode,
|
||||||
set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR);
|
set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR);
|
||||||
}
|
}
|
||||||
return page;
|
return page;
|
||||||
|
|
||||||
put_err:
|
|
||||||
f2fs_put_dnode(&dn);
|
|
||||||
return ERR_PTR(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __allocate_data_block(struct dnode_of_data *dn)
|
static int __allocate_data_block(struct dnode_of_data *dn)
|
||||||
|
|
Loading…
Reference in New Issue