[PATCH] f2fs: use f2fs_iget_nowait again

From: Jaegeuk Kim
Date: Mon Apr 29 2013 - 06:22:29 EST


Previously, I removed f2fs_iget_nowait() through the commit:
d4686d56ec912d55fd8a9d6d509de50de24e90ab
f2fs: avoid balanc_fs during evict_inode.

But, there is another error scenario as follows.

Thread 1:
- writeback_sb_inodes
- do_writepages
- f2fs_write_data_pages
- write_cache_pages
- f2fs_write_data_page
- f2fs_balance_fs
- wait mutex_lock(gc_mutex)

Thread 2:
- f2fs_balance_fs
- mutex_lock(gc_mutex)
- f2fs_gc
- f2fs_iget
- wait iget_locked(inode->i_lock)

Thread 3:
- do_unlinkat
- iput
- lock(inode->i_lock)
- evict
- inode_wait_for_writeback

The above three threads are able to incur deadlock condition.
In order to address this issue, let's use f2fs_iget_nowait again in f2fs_gc so
that thread 2 doesn't need to wait for inode->i_lock.

Signed-off-by: Jaegeuk Kim <jaegeuk.kim@xxxxxxxxxxx>
---
fs/f2fs/f2fs.h | 1 +
fs/f2fs/gc.c | 2 +-
fs/f2fs/inode.c | 33 +++++++++++++++++++++++++++++++++
3 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 20aab02..6fd872f 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -893,6 +893,7 @@ long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long);
* inode.c
*/
void f2fs_set_inode_flags(struct inode *);
+struct inode *f2fs_iget_nowait(struct super_block *, unsigned long);
struct inode *f2fs_iget(struct super_block *, unsigned long);
void update_inode(struct inode *, struct page *);
int update_inode_page(struct inode *);
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 25a1f7e..4b43b3f 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -580,7 +580,7 @@ next_step:
ofs_in_node = le16_to_cpu(entry->ofs_in_node);

if (phase == 2) {
- inode = f2fs_iget(sb, dni.ino);
+ inode = f2fs_iget_nowait(sb, dni.ino);
if (IS_ERR(inode))
continue;

diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 91ac7f9..a0879c3 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -18,6 +18,11 @@

#include <trace/events/f2fs.h>

+struct f2fs_iget_args {
+ u64 ino;
+ int on_free;
+};
+
void f2fs_set_inode_flags(struct inode *inode)
{
unsigned int flags = F2FS_I(inode)->i_flags;
@@ -37,6 +42,34 @@ void f2fs_set_inode_flags(struct inode *inode)
inode->i_flags |= S_DIRSYNC;
}

+static int f2fs_iget_test(struct inode *inode, void *data)
+{
+ struct f2fs_iget_args *args = data;
+
+ if (inode->i_ino != args->ino)
+ return 0;
+ if (inode->i_state & (I_FREEING | I_WILL_FREE)) {
+ args->on_free = 1;
+ return 0;
+ }
+ return 1;
+}
+
+struct inode *f2fs_iget_nowait(struct super_block *sb, unsigned long ino)
+{
+ struct f2fs_iget_args args = {
+ .ino = ino,
+ .on_free = 0
+ };
+ struct inode *inode = ilookup5(sb, ino, f2fs_iget_test, &args);
+
+ if (inode)
+ return inode;
+ if (!args.on_free)
+ return f2fs_iget(sb, ino);
+ return ERR_PTR(-ENOENT);
+}
+
static int do_read_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
--
1.8.1.3.566.gaa39828

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/