diff options
author | Jaegeuk Kim <jaegeuk@kernel.org> | 2015-04-29 18:18:42 (GMT) |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2015-05-28 22:41:35 (GMT) |
commit | 01f28610a1691078d0f7ba62b365567f8799f07c (patch) | |
tree | 5a97946706daa2904f3f112bd3c5025153109785 /fs/f2fs | |
parent | eaa693f4dcf1ca64b41b02d907e9c401c085cf59 (diff) | |
download | linux-01f28610a1691078d0f7ba62b365567f8799f07c.tar.xz |
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>
Diffstat (limited to 'fs/f2fs')
-rw-r--r-- | fs/f2fs/data.c | 27 |
1 files changed, 12 insertions, 15 deletions
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 81d1fd5..9ba30b4 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1076,20 +1076,22 @@ struct page *get_new_data_page(struct inode *inode, struct page *page; struct dnode_of_data dn; int err; +repeat: + page = grab_cache_page(mapping, index); + if (!page) + return ERR_PTR(-ENOMEM); set_new_dnode(&dn, inode, ipage, NULL, 0); err = f2fs_reserve_block(&dn, index); - if (err) + if (err) { + f2fs_put_page(page, 1); 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)) - return page; + goto got_it; if (dn.data_blkaddr == NEW_ADDR) { zero_user_segment(page, 0, PAGE_CACHE_SIZE); @@ -1104,20 +1106,19 @@ repeat: }; err = f2fs_submit_page_bio(&fio); if (err) - goto put_err; + return ERR_PTR(err); lock_page(page); if (unlikely(!PageUptodate(page))) { f2fs_put_page(page, 1); - err = -EIO; - goto put_err; + return ERR_PTR(-EIO); } if (unlikely(page->mapping != mapping)) { f2fs_put_page(page, 1); goto repeat; } } - +got_it: if (new_i_size && i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT)); @@ -1125,10 +1126,6 @@ repeat: set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); } return page; - -put_err: - f2fs_put_dnode(&dn); - return ERR_PTR(err); } static int __allocate_data_block(struct dnode_of_data *dn) |