f2fs: avoid deadlock during evict after f2fs_gc
authorJaegeuk Kim <jaegeuk.kim@samsung.com>
Tue, 30 Apr 2013 02:33:27 +0000 (11:33 +0900)
committerJaegeuk Kim <jaegeuk.kim@samsung.com>
Wed, 8 May 2013 10:54:08 +0000 (19:54 +0900)
o Deadlock case #1

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

o Deadlock case #2

Thread 1:
- __writeback_single_inode
 : set I_SYNC
  - do_writepages
   - f2fs_write_data_page
    - f2fs_balance_fs
     - f2fs_gc
      - iput
       - evict
        - inode_wait_for_writeback(I_SYNC)

In order to avoid this, even though iput is called with the zero-reference
count, we need to stop the eviction procedure if the inode is on writeback.
So this patch links f2fs_drop_inode which checks the I_SYNC flag.

Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
fs/f2fs/data.c
fs/f2fs/namei.c
fs/f2fs/super.c

index 2db9380f5ddaf026df5771fa8c4cecb3274734ef..61b44542417cd09cac3084c8be719b974117c086 100644 (file)
@@ -577,6 +577,7 @@ static int f2fs_write_data_pages(struct address_space *mapping,
 {
        struct inode *inode = mapping->host;
        struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
+       bool locked = false;
        int ret;
        long excess_nrtw = 0, desired_nrtw;
 
@@ -590,10 +591,12 @@ static int f2fs_write_data_pages(struct address_space *mapping,
                wbc->nr_to_write = desired_nrtw;
        }
 
-       if (!S_ISDIR(inode->i_mode))
+       if (!S_ISDIR(inode->i_mode)) {
                mutex_lock(&sbi->writepages);
+               locked = true;
+       }
        ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
-       if (!S_ISDIR(inode->i_mode))
+       if (locked)
                mutex_unlock(&sbi->writepages);
        f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL));
 
index 4aa26e53c935338b8b063297287350d8689ee337..47abc9722b17abfae9656b3d5d52360fc8e72ad8 100644 (file)
@@ -72,6 +72,7 @@ out:
        unlock_new_inode(inode);
 fail:
        trace_f2fs_new_inode(inode, err);
+       make_bad_inode(inode);
        iput(inode);
        if (nid_free)
                alloc_nid_failed(sbi, ino);
@@ -155,6 +156,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 out:
        clear_nlink(inode);
        unlock_new_inode(inode);
+       make_bad_inode(inode);
        iput(inode);
        alloc_nid_failed(sbi, ino);
        return err;
@@ -190,6 +192,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
        return 0;
 out:
        clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
+       make_bad_inode(inode);
        iput(inode);
        return err;
 }
@@ -295,6 +298,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 out:
        clear_nlink(inode);
        unlock_new_inode(inode);
+       make_bad_inode(inode);
        iput(inode);
        alloc_nid_failed(sbi, inode->i_ino);
        return err;
@@ -335,6 +339,7 @@ out_fail:
        clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
        clear_nlink(inode);
        unlock_new_inode(inode);
+       make_bad_inode(inode);
        iput(inode);
        alloc_nid_failed(sbi, inode->i_ino);
        return err;
@@ -382,6 +387,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
 out:
        clear_nlink(inode);
        unlock_new_inode(inode);
+       make_bad_inode(inode);
        iput(inode);
        alloc_nid_failed(sbi, inode->i_ino);
        return err;
index 5835aaf2fe2e3d0ababca0ffcf5b5e0545d39456..cd0e89a1ff8f18c4c51668f2aec0f2fda89b43c1 100644 (file)
@@ -98,6 +98,20 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
        return &fi->vfs_inode;
 }
 
+static int f2fs_drop_inode(struct inode *inode)
+{
+       /*
+        * This is to avoid a deadlock condition like below.
+        * writeback_single_inode(inode)
+        *  - f2fs_write_data_page
+        *    - f2fs_gc -> iput -> evict
+        *       - inode_wait_for_writeback(inode)
+        */
+       if (!inode_unhashed(inode) && inode->i_state & I_SYNC)
+               return 0;
+       return generic_drop_inode(inode);
+}
+
 static void f2fs_i_callback(struct rcu_head *head)
 {
        struct inode *inode = container_of(head, struct inode, i_rcu);
@@ -232,6 +246,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
 
 static struct super_operations f2fs_sops = {
        .alloc_inode    = f2fs_alloc_inode,
+       .drop_inode     = f2fs_drop_inode,
        .destroy_inode  = f2fs_destroy_inode,
        .write_inode    = f2fs_write_inode,
        .show_options   = f2fs_show_options,