diff options
| author | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-04-29 22:33:27 -0400 |
|---|---|---|
| committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-05-08 06:54:08 -0400 |
| commit | 531ad7d58c6476c5856653448b4c7d26427502b4 (patch) | |
| tree | 6b890195ebfd773fa55ec580ce2248a2a47d5452 | |
| parent | ac5d156c78a68b39955ee9b09498ba93831c77d7 (diff) | |
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: Jaegeuk Kim <jaegeuk.kim@samsung.com>
| -rw-r--r-- | fs/f2fs/data.c | 7 | ||||
| -rw-r--r-- | fs/f2fs/namei.c | 6 | ||||
| -rw-r--r-- | fs/f2fs/super.c | 15 |
3 files changed, 26 insertions, 2 deletions
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 2db9380f5dda..61b44542417c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c | |||
| @@ -577,6 +577,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, | |||
| 577 | { | 577 | { |
| 578 | struct inode *inode = mapping->host; | 578 | struct inode *inode = mapping->host; |
| 579 | struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); | 579 | struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); |
| 580 | bool locked = false; | ||
| 580 | int ret; | 581 | int ret; |
| 581 | long excess_nrtw = 0, desired_nrtw; | 582 | long excess_nrtw = 0, desired_nrtw; |
| 582 | 583 | ||
| @@ -590,10 +591,12 @@ static int f2fs_write_data_pages(struct address_space *mapping, | |||
| 590 | wbc->nr_to_write = desired_nrtw; | 591 | wbc->nr_to_write = desired_nrtw; |
| 591 | } | 592 | } |
| 592 | 593 | ||
| 593 | if (!S_ISDIR(inode->i_mode)) | 594 | if (!S_ISDIR(inode->i_mode)) { |
| 594 | mutex_lock(&sbi->writepages); | 595 | mutex_lock(&sbi->writepages); |
| 596 | locked = true; | ||
| 597 | } | ||
| 595 | ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); | 598 | ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); |
| 596 | if (!S_ISDIR(inode->i_mode)) | 599 | if (locked) |
| 597 | mutex_unlock(&sbi->writepages); | 600 | mutex_unlock(&sbi->writepages); |
| 598 | f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL)); | 601 | f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL)); |
| 599 | 602 | ||
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 4aa26e53c935..47abc9722b17 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c | |||
| @@ -72,6 +72,7 @@ out: | |||
| 72 | unlock_new_inode(inode); | 72 | unlock_new_inode(inode); |
| 73 | fail: | 73 | fail: |
| 74 | trace_f2fs_new_inode(inode, err); | 74 | trace_f2fs_new_inode(inode, err); |
| 75 | make_bad_inode(inode); | ||
| 75 | iput(inode); | 76 | iput(inode); |
| 76 | if (nid_free) | 77 | if (nid_free) |
| 77 | alloc_nid_failed(sbi, ino); | 78 | alloc_nid_failed(sbi, ino); |
| @@ -155,6 +156,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, | |||
| 155 | out: | 156 | out: |
| 156 | clear_nlink(inode); | 157 | clear_nlink(inode); |
| 157 | unlock_new_inode(inode); | 158 | unlock_new_inode(inode); |
| 159 | make_bad_inode(inode); | ||
| 158 | iput(inode); | 160 | iput(inode); |
| 159 | alloc_nid_failed(sbi, ino); | 161 | alloc_nid_failed(sbi, ino); |
| 160 | return err; | 162 | return err; |
| @@ -190,6 +192,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, | |||
| 190 | return 0; | 192 | return 0; |
| 191 | out: | 193 | out: |
| 192 | clear_inode_flag(F2FS_I(inode), FI_INC_LINK); | 194 | clear_inode_flag(F2FS_I(inode), FI_INC_LINK); |
| 195 | make_bad_inode(inode); | ||
| 193 | iput(inode); | 196 | iput(inode); |
| 194 | return err; | 197 | return err; |
| 195 | } | 198 | } |
| @@ -295,6 +298,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, | |||
| 295 | out: | 298 | out: |
| 296 | clear_nlink(inode); | 299 | clear_nlink(inode); |
| 297 | unlock_new_inode(inode); | 300 | unlock_new_inode(inode); |
| 301 | make_bad_inode(inode); | ||
| 298 | iput(inode); | 302 | iput(inode); |
| 299 | alloc_nid_failed(sbi, inode->i_ino); | 303 | alloc_nid_failed(sbi, inode->i_ino); |
| 300 | return err; | 304 | return err; |
| @@ -335,6 +339,7 @@ out_fail: | |||
| 335 | clear_inode_flag(F2FS_I(inode), FI_INC_LINK); | 339 | clear_inode_flag(F2FS_I(inode), FI_INC_LINK); |
| 336 | clear_nlink(inode); | 340 | clear_nlink(inode); |
| 337 | unlock_new_inode(inode); | 341 | unlock_new_inode(inode); |
| 342 | make_bad_inode(inode); | ||
| 338 | iput(inode); | 343 | iput(inode); |
| 339 | alloc_nid_failed(sbi, inode->i_ino); | 344 | alloc_nid_failed(sbi, inode->i_ino); |
| 340 | return err; | 345 | return err; |
| @@ -382,6 +387,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, | |||
| 382 | out: | 387 | out: |
| 383 | clear_nlink(inode); | 388 | clear_nlink(inode); |
| 384 | unlock_new_inode(inode); | 389 | unlock_new_inode(inode); |
| 390 | make_bad_inode(inode); | ||
| 385 | iput(inode); | 391 | iput(inode); |
| 386 | alloc_nid_failed(sbi, inode->i_ino); | 392 | alloc_nid_failed(sbi, inode->i_ino); |
| 387 | return err; | 393 | return err; |
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 5835aaf2fe2e..cd0e89a1ff8f 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c | |||
| @@ -98,6 +98,20 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) | |||
| 98 | return &fi->vfs_inode; | 98 | return &fi->vfs_inode; |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | static int f2fs_drop_inode(struct inode *inode) | ||
| 102 | { | ||
| 103 | /* | ||
| 104 | * This is to avoid a deadlock condition like below. | ||
| 105 | * writeback_single_inode(inode) | ||
| 106 | * - f2fs_write_data_page | ||
| 107 | * - f2fs_gc -> iput -> evict | ||
| 108 | * - inode_wait_for_writeback(inode) | ||
| 109 | */ | ||
| 110 | if (!inode_unhashed(inode) && inode->i_state & I_SYNC) | ||
| 111 | return 0; | ||
| 112 | return generic_drop_inode(inode); | ||
| 113 | } | ||
| 114 | |||
| 101 | static void f2fs_i_callback(struct rcu_head *head) | 115 | static void f2fs_i_callback(struct rcu_head *head) |
| 102 | { | 116 | { |
| 103 | struct inode *inode = container_of(head, struct inode, i_rcu); | 117 | 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 | 246 | ||
| 233 | static struct super_operations f2fs_sops = { | 247 | static struct super_operations f2fs_sops = { |
| 234 | .alloc_inode = f2fs_alloc_inode, | 248 | .alloc_inode = f2fs_alloc_inode, |
| 249 | .drop_inode = f2fs_drop_inode, | ||
| 235 | .destroy_inode = f2fs_destroy_inode, | 250 | .destroy_inode = f2fs_destroy_inode, |
| 236 | .write_inode = f2fs_write_inode, | 251 | .write_inode = f2fs_write_inode, |
| 237 | .show_options = f2fs_show_options, | 252 | .show_options = f2fs_show_options, |
