f2fs: update inode page after creation

I found a bug when testing power-off-recovery as follows.

[Bug Scenario]
1. create a file
2. fsync the file
3. reboot w/o any sync
4. try to recover the file
 - found its fsync mark
 - found its dentry mark
   : try to recover its dentry
    - get its file name
    - get its parent inode number
     : here we got zero value

The reason why we get the wrong parent inode number is that we didn't
synchronize the inode page with its newly created inode information perfectly.

Especially, previous f2fs stores fi->i_pino and writes it to the cached
node page in a wrong order, which incurs the zero-valued i_pino during the
recovery.

So, this patch modifies the creation flow to fix the synchronization order of
inode page with its inode.

Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
This commit is contained in:
Jaegeuk Kim 2013-05-20 10:10:29 +09:00
parent 64aa7ed98d
commit 44a83ff6a8
4 changed files with 51 additions and 50 deletions

View File

@ -279,6 +279,7 @@ repeat:
*
* Also, caller should grab and release a mutex by calling mutex_lock_op() and
* mutex_unlock_op().
* Note that, npage is set only by make_empty_dir.
*/
struct page *get_new_data_page(struct inode *inode,
struct page *npage, pgoff_t index, bool new_i_size)

View File

@ -264,15 +264,10 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,
f2fs_put_page(page, 1);
}
void init_dent_inode(const struct qstr *name, struct page *ipage)
static void init_dent_inode(const struct qstr *name, struct page *ipage)
{
struct f2fs_node *rn;
if (IS_ERR(ipage))
return;
wait_on_page_writeback(ipage);
/* copy name info. to this inode page */
rn = (struct f2fs_node *)page_address(ipage);
rn->i.i_namelen = cpu_to_le32(name->len);
@ -280,14 +275,15 @@ void init_dent_inode(const struct qstr *name, struct page *ipage)
set_page_dirty(ipage);
}
static int make_empty_dir(struct inode *inode, struct inode *parent)
static int make_empty_dir(struct inode *inode,
struct inode *parent, struct page *page)
{
struct page *dentry_page;
struct f2fs_dentry_block *dentry_blk;
struct f2fs_dir_entry *de;
void *kaddr;
dentry_page = get_new_data_page(inode, NULL, 0, true);
dentry_page = get_new_data_page(inode, page, 0, true);
if (IS_ERR(dentry_page))
return PTR_ERR(dentry_page);
@ -317,42 +313,47 @@ static int make_empty_dir(struct inode *inode, struct inode *parent)
return 0;
}
static int init_inode_metadata(struct inode *inode,
static struct page *init_inode_metadata(struct inode *inode,
struct inode *dir, const struct qstr *name)
{
struct page *page;
int err;
if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) {
int err;
err = new_inode_page(inode, name);
if (err)
return err;
page = new_inode_page(inode, name);
if (IS_ERR(page))
return page;
if (S_ISDIR(inode->i_mode)) {
err = make_empty_dir(inode, dir);
if (err) {
remove_inode_page(inode);
return err;
}
err = make_empty_dir(inode, dir, page);
if (err)
goto error;
}
err = f2fs_init_acl(inode, dir);
if (err) {
remove_inode_page(inode);
return err;
}
if (err)
goto error;
wait_on_page_writeback(page);
} else {
struct page *ipage;
ipage = get_node_page(F2FS_SB(dir->i_sb), inode->i_ino);
if (IS_ERR(ipage))
return PTR_ERR(ipage);
set_cold_node(inode, ipage);
init_dent_inode(name, ipage);
f2fs_put_page(ipage, 1);
page = get_node_page(F2FS_SB(dir->i_sb), inode->i_ino);
if (IS_ERR(page))
return page;
wait_on_page_writeback(page);
set_cold_node(inode, page);
}
if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) {
init_dent_inode(name, page);
if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK))
inc_nlink(inode);
update_inode_page(inode);
}
return 0;
return page;
error:
f2fs_put_page(page, 1);
remove_inode_page(inode);
return ERR_PTR(err);
}
static void update_parent_metadata(struct inode *dir, struct inode *inode,
@ -423,6 +424,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in
struct page *dentry_page = NULL;
struct f2fs_dentry_block *dentry_blk = NULL;
int slots = GET_DENTRY_SLOTS(namelen);
struct page *page;
int err = 0;
int i;
@ -465,12 +467,13 @@ start:
++level;
goto start;
add_dentry:
err = init_inode_metadata(inode, dir, name);
if (err)
goto fail;
wait_on_page_writeback(dentry_page);
page = init_inode_metadata(inode, dir, name);
if (IS_ERR(page)) {
err = PTR_ERR(page);
goto fail;
}
de = &dentry_blk->dentry[bit_pos];
de->hash_code = dentry_hash;
de->name_len = cpu_to_le16(namelen);
@ -481,10 +484,12 @@ add_dentry:
test_and_set_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap);
set_page_dirty(dentry_page);
update_parent_metadata(dir, inode, current_depth);
/* update parent inode number before releasing dentry page */
/* we don't need to mark_inode_dirty now */
F2FS_I(inode)->i_pino = dir->i_ino;
update_inode(inode, page);
f2fs_put_page(page, 1);
update_parent_metadata(dir, inode, current_depth);
fail:
kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);

View File

@ -914,7 +914,6 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **);
ino_t f2fs_inode_by_name(struct inode *, struct qstr *);
void f2fs_set_link(struct inode *, struct f2fs_dir_entry *,
struct page *, struct inode *);
void init_dent_inode(const struct qstr *, struct page *);
int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *);
void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *);
int f2fs_make_empty(struct inode *, struct inode *);
@ -949,7 +948,7 @@ void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *);
int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int);
int truncate_inode_blocks(struct inode *, pgoff_t);
int remove_inode_page(struct inode *);
int new_inode_page(struct inode *, const struct qstr *);
struct page *new_inode_page(struct inode *, const struct qstr *);
struct page *new_node_page(struct dnode_of_data *, unsigned int);
void ra_node_page(struct f2fs_sb_info *, nid_t);
struct page *get_node_page(struct f2fs_sb_info *, pgoff_t);

View File

@ -806,19 +806,15 @@ int remove_inode_page(struct inode *inode)
return 0;
}
int new_inode_page(struct inode *inode, const struct qstr *name)
struct page *new_inode_page(struct inode *inode, const struct qstr *name)
{
struct page *page;
struct dnode_of_data dn;
/* allocate inode page for new inode */
set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino);
page = new_node_page(&dn, 0);
init_dent_inode(name, page);
if (IS_ERR(page))
return PTR_ERR(page);
f2fs_put_page(page, 1);
return 0;
/* caller should f2fs_put_page(page, 1); */
return new_node_page(&dn, 0);
}
struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs)