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:
Jaegeuk Kim 2016-05-02 12:34:48 -07:00
Родитель cb78942b82
Коммит 221149c00e
2 изменённых файлов: 28 добавлений и 32 удалений

Просмотреть файл

@ -391,9 +391,14 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
return page; return page;
if (S_ISDIR(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) {
/* in order to handle error case */
get_page(page);
err = make_empty_dir(inode, dir, page); err = make_empty_dir(inode, dir, page);
if (err) if (err) {
goto error; lock_page(page);
goto put_error;
}
put_page(page);
} }
err = f2fs_init_acl(inode, dir, page, dpage); 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; return page;
put_error: put_error:
f2fs_put_page(page, 1); /* truncate empty dir pages */
error:
/* once the failed inode becomes a bad inode, i_mode is S_IFREG */
truncate_inode_pages(&inode->i_data, 0); truncate_inode_pages(&inode->i_data, 0);
truncate_blocks(inode, 0, false);
remove_dirty_inode(inode); clear_nlink(inode);
remove_inode_page(inode); update_inode(inode, page);
f2fs_put_page(page, 1);
return ERR_PTR(err); return ERR_PTR(err);
} }

Просмотреть файл

@ -368,10 +368,7 @@ no_delete:
if (is_inode_flag_set(fi, FI_UPDATE_WRITE)) if (is_inode_flag_set(fi, FI_UPDATE_WRITE))
add_ino_entry(sbi, inode->i_ino, UPDATE_INO); add_ino_entry(sbi, inode->i_ino, UPDATE_INO);
if (is_inode_flag_set(fi, FI_FREE_NID)) { if (is_inode_flag_set(fi, FI_FREE_NID)) {
if (err && err != -ENOENT) alloc_nid_failed(sbi, inode->i_ino);
alloc_nid_done(sbi, inode->i_ino);
else
alloc_nid_failed(sbi, inode->i_ino);
clear_inode_flag(fi, FI_FREE_NID); clear_inode_flag(fi, FI_FREE_NID);
} }
@ -397,37 +394,32 @@ out_clear:
void handle_failed_inode(struct inode *inode) void handle_failed_inode(struct inode *inode)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
int err = 0; struct node_info ni;
clear_nlink(inode); /* don't make bad inode, since it becomes a regular file. */
make_bad_inode(inode);
unlock_new_inode(inode); 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() * Note: we should add inode to orphan list before f2fs_unlock_op()
* so we can prevent losing this orphan when encoutering checkpoint * so we can prevent losing this orphan when encoutering checkpoint
* and following suddenly power-off. * and following suddenly power-off.
*/ */
if (err && err != -ENOENT) { get_node_info(sbi, inode->i_ino, &ni);
err = acquire_orphan_inode(sbi);
if (!err) 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); 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); f2fs_unlock_op(sbi);
/* iput will drop the inode object */ /* iput will drop the inode object */