2013-05-28 01:21:05

by Jaegeuk Kim

[permalink] [raw]
Subject: [PATCH 1/3] f2fs: iput only if whole data blocks are flushed

If there remains some unwritten blocks from the recovery, we should not call
iput on that directory inode.
Otherwise, we can loose some dentry blocks after the recovery.

Signed-off-by: Jaegeuk Kim <[email protected]>
---
fs/f2fs/checkpoint.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 01ddc91..0d3701d 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -501,8 +501,10 @@ void remove_dirty_dir_inode(struct inode *inode)
return;

spin_lock(&sbi->dir_inode_lock);
- if (atomic_read(&F2FS_I(inode)->dirty_dents))
- goto out;
+ if (atomic_read(&F2FS_I(inode)->dirty_dents)) {
+ spin_unlock(&sbi->dir_inode_lock);
+ return;
+ }

list_for_each(this, head) {
struct dir_inode_entry *entry;
@@ -516,7 +518,6 @@ void remove_dirty_dir_inode(struct inode *inode)
break;
}
}
-out:
spin_unlock(&sbi->dir_inode_lock);

/* Only from the recovery routine */
--
1.8.1.3.566.gaa39828


2013-05-28 01:21:07

by Jaegeuk Kim

[permalink] [raw]
Subject: [PATCH 2/3] f2fs: fix dentry recovery routine

The error scenario is:
1. create /a
(1.a link /a /b)
2. sync
3. unlinke /a
4. create /a
5. fsync /a
6. Sudden power-off

When the f2fs recovers the fsynced dentry, /a, we discover an exsiting dentry at
f2fs_find_entry() in recover_dentry().

In such the case, we should unlink the existing dentry and its inode
and then recover newly created dentry.

Signed-off-by: Jaegeuk Kim <[email protected]>
---
fs/f2fs/recovery.c | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index 0dd2ce1..85f5e54 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -44,9 +44,10 @@ static int recover_dentry(struct page *ipage, struct inode *inode)
struct f2fs_node *raw_node = (struct f2fs_node *)kaddr;
struct f2fs_inode *raw_inode = &(raw_node->i);
nid_t pino = le32_to_cpu(raw_inode->i_pino);
+ struct f2fs_dir_entry *de;
struct qstr name;
struct page *page;
- struct inode *dir;
+ struct inode *dir, *einode;
int err = 0;

dir = check_dirty_dir_inode(F2FS_SB(inode->i_sb), pino);
@@ -61,13 +62,21 @@ static int recover_dentry(struct page *ipage, struct inode *inode)

name.len = le32_to_cpu(raw_inode->i_namelen);
name.name = raw_inode->i_name;
-
- if (f2fs_find_entry(dir, &name, &page)) {
- kunmap(page);
- f2fs_put_page(page, 0);
- } else {
- err = __f2fs_add_link(dir, &name, inode);
+retry:
+ de = f2fs_find_entry(dir, &name, &page);
+ if (de) {
+ einode = f2fs_iget(inode->i_sb, le32_to_cpu(de->ino));
+ if (IS_ERR(einode)) {
+ WARN_ON(1);
+ if (PTR_ERR(einode) == -ENOENT)
+ err = -EEXIST;
+ goto out;
+ }
+ f2fs_delete_entry(de, page, einode);
+ iput(einode);
+ goto retry;
}
+ err = __f2fs_add_link(dir, &name, inode);
out:
f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode and its dentry: "
"ino = %x, name = %s, dir = %lx, err = %d",
--
1.8.1.3.566.gaa39828

2013-05-28 01:21:40

by Jaegeuk Kim

[permalink] [raw]
Subject: [PATCH 3/3] f2fs: fix incorrect iputs during the dentry recovery

- iget/iput flow in the dentry recovery process

1. *dir* = f2fs_iget
2. set FI_DELAY_IPUT to *dir*
3. add *dir* to the dirty_dir_list
- __f2fs_add_link
- recover_dentry)
4. iput *dir* by remove_dirty_dir_inode
- sync_dirty_dir_inodes
- write_chekcpoint

If *dir*'s i_count is not 1 (i.e., root dir), remove_dirty_dir_inode is called
later and then iput is triggered again due to the FI_DELAY_IPUT flag.
So, let's unset the flag properly once iput is triggered.

Signed-off-by: Jaegeuk Kim <[email protected]>
---
fs/f2fs/checkpoint.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 0d3701d..6f56e57 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -521,8 +521,10 @@ void remove_dirty_dir_inode(struct inode *inode)
spin_unlock(&sbi->dir_inode_lock);

/* Only from the recovery routine */
- if (is_inode_flag_set(F2FS_I(inode), FI_DELAY_IPUT))
+ if (is_inode_flag_set(F2FS_I(inode), FI_DELAY_IPUT)) {
+ clear_inode_flag(F2FS_I(inode), FI_DELAY_IPUT);
iput(inode);
+ }
}

struct inode *check_dirty_dir_inode(struct f2fs_sb_info *sbi, nid_t ino)
--
1.8.1.3.566.gaa39828