Commit 531ad7d5 authored by Jaegeuk Kim's avatar Jaegeuk Kim

f2fs: avoid deadlock during evict after f2fs_gc

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: default avatarJaegeuk Kim <jaegeuk.kim@samsung.com>
parent ac5d156c
...@@ -577,6 +577,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, ...@@ -577,6 +577,7 @@ static int f2fs_write_data_pages(struct address_space *mapping,
{ {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
bool locked = false;
int ret; int ret;
long excess_nrtw = 0, desired_nrtw; long excess_nrtw = 0, desired_nrtw;
...@@ -590,10 +591,12 @@ static int f2fs_write_data_pages(struct address_space *mapping, ...@@ -590,10 +591,12 @@ static int f2fs_write_data_pages(struct address_space *mapping,
wbc->nr_to_write = desired_nrtw; wbc->nr_to_write = desired_nrtw;
} }
if (!S_ISDIR(inode->i_mode)) if (!S_ISDIR(inode->i_mode)) {
mutex_lock(&sbi->writepages); mutex_lock(&sbi->writepages);
locked = true;
}
ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
if (!S_ISDIR(inode->i_mode)) if (locked)
mutex_unlock(&sbi->writepages); mutex_unlock(&sbi->writepages);
f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL)); f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL));
......
...@@ -72,6 +72,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) ...@@ -72,6 +72,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
unlock_new_inode(inode); unlock_new_inode(inode);
fail: fail:
trace_f2fs_new_inode(inode, err); trace_f2fs_new_inode(inode, err);
make_bad_inode(inode);
iput(inode); iput(inode);
if (nid_free) if (nid_free)
alloc_nid_failed(sbi, ino); alloc_nid_failed(sbi, ino);
...@@ -155,6 +156,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -155,6 +156,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
out: out:
clear_nlink(inode); clear_nlink(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
make_bad_inode(inode);
iput(inode); iput(inode);
alloc_nid_failed(sbi, ino); alloc_nid_failed(sbi, ino);
return err; return err;
...@@ -190,6 +192,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, ...@@ -190,6 +192,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
return 0; return 0;
out: out:
clear_inode_flag(F2FS_I(inode), FI_INC_LINK); clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
make_bad_inode(inode);
iput(inode); iput(inode);
return err; return err;
} }
...@@ -295,6 +298,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -295,6 +298,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
out: out:
clear_nlink(inode); clear_nlink(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
make_bad_inode(inode);
iput(inode); iput(inode);
alloc_nid_failed(sbi, inode->i_ino); alloc_nid_failed(sbi, inode->i_ino);
return err; return err;
...@@ -335,6 +339,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -335,6 +339,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
clear_inode_flag(F2FS_I(inode), FI_INC_LINK); clear_inode_flag(F2FS_I(inode), FI_INC_LINK);
clear_nlink(inode); clear_nlink(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
make_bad_inode(inode);
iput(inode); iput(inode);
alloc_nid_failed(sbi, inode->i_ino); alloc_nid_failed(sbi, inode->i_ino);
return err; return err;
...@@ -382,6 +387,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, ...@@ -382,6 +387,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
out: out:
clear_nlink(inode); clear_nlink(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
make_bad_inode(inode);
iput(inode); iput(inode);
alloc_nid_failed(sbi, inode->i_ino); alloc_nid_failed(sbi, inode->i_ino);
return err; return err;
......
...@@ -98,6 +98,20 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) ...@@ -98,6 +98,20 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
return &fi->vfs_inode; 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) static void f2fs_i_callback(struct rcu_head *head)
{ {
struct inode *inode = container_of(head, struct inode, i_rcu); 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) ...@@ -232,6 +246,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
static struct super_operations f2fs_sops = { static struct super_operations f2fs_sops = {
.alloc_inode = f2fs_alloc_inode, .alloc_inode = f2fs_alloc_inode,
.drop_inode = f2fs_drop_inode,
.destroy_inode = f2fs_destroy_inode, .destroy_inode = f2fs_destroy_inode,
.write_inode = f2fs_write_inode, .write_inode = f2fs_write_inode,
.show_options = f2fs_show_options, .show_options = f2fs_show_options,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment