diff options
author | Jaegeuk Kim <jaegeuk@kernel.org> | 2014-09-12 11:35:58 -0400 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2014-09-16 07:10:47 -0400 |
commit | 60979115a69e0e7916a1c1796f902264f1350977 (patch) | |
tree | 2ad30495672aee3a7e7822b7cd3eb5c2fdcbd543 /fs/f2fs/recovery.c | |
parent | c6e489305eb5ed029002b037e36800032a994bb4 (diff) |
f2fs: fix double lock for inode page during roll-foward recovery
If the inode is same and its data index are needed to truncate, we can fall into
double lock for its inode page via get_dnode_of_data.
Error case is like this.
1. write data 1, 2, 3, 4, 5 in inode #4.
2. write data 100, 102, 103, 104, 105 in dnode #6 of inode #4.
3. sync
4. update data 100->106 in dnode #6.
5. fsync inode #4.
6. power-cut
-> Then,
1. go back to #3's checkpoint
2. in do_recover_data, get_dnode_of_data() gets inode #4.
3. detect 100->106 in dnode #6.
4. check_index_in_prev_nodes tries to truncate 100 in dnode #6.
5. to trigger truncate_hole, get_dnode_of_data should grab inode #4.
6. detect *kernel hang*
This patch should resolve that bug.
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Diffstat (limited to 'fs/f2fs/recovery.c')
-rw-r--r-- | fs/f2fs/recovery.c | 28 |
1 files changed, 21 insertions, 7 deletions
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 6c5a74a45f33..e587ee128e17 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c | |||
@@ -279,16 +279,30 @@ got_it: | |||
279 | ino = ino_of_node(node_page); | 279 | ino = ino_of_node(node_page); |
280 | f2fs_put_page(node_page, 1); | 280 | f2fs_put_page(node_page, 1); |
281 | 281 | ||
282 | /* Deallocate previous index in the node page */ | 282 | if (ino != dn->inode->i_ino) { |
283 | inode = f2fs_iget(sbi->sb, ino); | 283 | /* Deallocate previous index in the node page */ |
284 | if (IS_ERR(inode)) | 284 | inode = f2fs_iget(sbi->sb, ino); |
285 | return PTR_ERR(inode); | 285 | if (IS_ERR(inode)) |
286 | return PTR_ERR(inode); | ||
287 | } else { | ||
288 | inode = dn->inode; | ||
289 | } | ||
286 | 290 | ||
287 | bidx = start_bidx_of_node(offset, F2FS_I(inode)) + | 291 | bidx = start_bidx_of_node(offset, F2FS_I(inode)) + |
288 | le16_to_cpu(sum.ofs_in_node); | 292 | le16_to_cpu(sum.ofs_in_node); |
289 | 293 | ||
290 | truncate_hole(inode, bidx, bidx + 1); | 294 | if (ino != dn->inode->i_ino) { |
291 | iput(inode); | 295 | truncate_hole(inode, bidx, bidx + 1); |
296 | iput(inode); | ||
297 | } else { | ||
298 | struct dnode_of_data tdn; | ||
299 | set_new_dnode(&tdn, inode, dn->inode_page, NULL, 0); | ||
300 | if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE)) | ||
301 | return 0; | ||
302 | if (tdn.data_blkaddr != NULL_ADDR) | ||
303 | truncate_data_blocks_range(&tdn, 1); | ||
304 | f2fs_put_page(tdn.node_page, 1); | ||
305 | } | ||
292 | return 0; | 306 | return 0; |
293 | } | 307 | } |
294 | 308 | ||