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 /fs/f2fs | |
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>
Diffstat (limited to 'fs/f2fs')
-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, |