f2fs: revisit error handling flows
This patch fixes a couple of bugs regarding to orphan inodes when handling errors. This tries to - call alloc_nid_done with add_orphan_inode in handle_failed_inode - let truncate blocks in f2fs_evict_inode - not make a bad inode due to i_mode change Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
cb78942b82
commit
221149c00e
|
@ -391,9 +391,14 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
|
|||
return page;
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
/* in order to handle error case */
|
||||
get_page(page);
|
||||
err = make_empty_dir(inode, dir, page);
|
||||
if (err)
|
||||
goto error;
|
||||
if (err) {
|
||||
lock_page(page);
|
||||
goto put_error;
|
||||
}
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
err = f2fs_init_acl(inode, dir, page, dpage);
|
||||
|
@ -437,13 +442,12 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
|
|||
return page;
|
||||
|
||||
put_error:
|
||||
f2fs_put_page(page, 1);
|
||||
error:
|
||||
/* once the failed inode becomes a bad inode, i_mode is S_IFREG */
|
||||
/* truncate empty dir pages */
|
||||
truncate_inode_pages(&inode->i_data, 0);
|
||||
truncate_blocks(inode, 0, false);
|
||||
remove_dirty_inode(inode);
|
||||
remove_inode_page(inode);
|
||||
|
||||
clear_nlink(inode);
|
||||
update_inode(inode, page);
|
||||
f2fs_put_page(page, 1);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
|
|
@ -368,10 +368,7 @@ no_delete:
|
|||
if (is_inode_flag_set(fi, FI_UPDATE_WRITE))
|
||||
add_ino_entry(sbi, inode->i_ino, UPDATE_INO);
|
||||
if (is_inode_flag_set(fi, FI_FREE_NID)) {
|
||||
if (err && err != -ENOENT)
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
else
|
||||
alloc_nid_failed(sbi, inode->i_ino);
|
||||
alloc_nid_failed(sbi, inode->i_ino);
|
||||
clear_inode_flag(fi, FI_FREE_NID);
|
||||
}
|
||||
|
||||
|
@ -397,37 +394,32 @@ out_clear:
|
|||
void handle_failed_inode(struct inode *inode)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
int err = 0;
|
||||
struct node_info ni;
|
||||
|
||||
clear_nlink(inode);
|
||||
make_bad_inode(inode);
|
||||
/* don't make bad inode, since it becomes a regular file. */
|
||||
unlock_new_inode(inode);
|
||||
|
||||
i_size_write(inode, 0);
|
||||
if (F2FS_HAS_BLOCKS(inode))
|
||||
err = f2fs_truncate(inode, false);
|
||||
|
||||
if (!err)
|
||||
err = remove_inode_page(inode);
|
||||
|
||||
/*
|
||||
* if we skip truncate_node in remove_inode_page bacause we failed
|
||||
* before, it's better to find another way to release resource of
|
||||
* this inode (e.g. valid block count, node block or nid). Here we
|
||||
* choose to add this inode to orphan list, so that we can call iput
|
||||
* for releasing in orphan recovery flow.
|
||||
*
|
||||
* Note: we should add inode to orphan list before f2fs_unlock_op()
|
||||
* so we can prevent losing this orphan when encoutering checkpoint
|
||||
* and following suddenly power-off.
|
||||
*/
|
||||
if (err && err != -ENOENT) {
|
||||
err = acquire_orphan_inode(sbi);
|
||||
if (!err)
|
||||
get_node_info(sbi, inode->i_ino, &ni);
|
||||
|
||||
if (ni.blk_addr != NULL_ADDR) {
|
||||
int err = acquire_orphan_inode(sbi);
|
||||
if (err) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_msg(sbi->sb, KERN_WARNING,
|
||||
"Too many orphan inodes, run fsck to fix.");
|
||||
} else {
|
||||
add_orphan_inode(sbi, inode->i_ino);
|
||||
}
|
||||
alloc_nid_done(sbi, inode->i_ino);
|
||||
} else {
|
||||
set_inode_flag(F2FS_I(inode), FI_FREE_NID);
|
||||
}
|
||||
|
||||
set_inode_flag(F2FS_I(inode), FI_FREE_NID);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
/* iput will drop the inode object */
|
||||
|
|
Loading…
Reference in New Issue