diff options
author | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-07-29 22:36:53 -0400 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-07-30 02:17:03 -0400 |
commit | cbd56e7d20d7188d62a85aa6986a7b2c8e755ab5 (patch) | |
tree | 44962739f9ce15214fc5f0587b2174aae1f032f5 | |
parent | d8207f69589c74037128ff6c9e1a44223fad3b7c (diff) |
f2fs: fix handling orphan inodes
This patch fixes mishandling of the sbi->n_orphans variable.
If users request lots of f2fs_unlink(), check_orphan_space() could be contended.
In such the case, sbi->n_orphans can be read incorrectly so that f2fs_unlink()
would fall into the wrong state which results in the failure of
add_orphan_inode().
So, let's increment sbi->n_orphans virtually prior to the actual orphan inode
stuffs. After that, let's release sbi->n_orphans by calling release_orphan_inode
or remove_orphan_inode.
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
-rw-r--r-- | fs/f2fs/checkpoint.c | 13 | ||||
-rw-r--r-- | fs/f2fs/dir.c | 2 | ||||
-rw-r--r-- | fs/f2fs/f2fs.h | 3 | ||||
-rw-r--r-- | fs/f2fs/namei.c | 19 |
4 files changed, 28 insertions, 9 deletions
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index fe91773de130..c5a5c390a5cc 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c | |||
@@ -182,7 +182,7 @@ const struct address_space_operations f2fs_meta_aops = { | |||
182 | .set_page_dirty = f2fs_set_meta_page_dirty, | 182 | .set_page_dirty = f2fs_set_meta_page_dirty, |
183 | }; | 183 | }; |
184 | 184 | ||
185 | int check_orphan_space(struct f2fs_sb_info *sbi) | 185 | int acquire_orphan_inode(struct f2fs_sb_info *sbi) |
186 | { | 186 | { |
187 | unsigned int max_orphans; | 187 | unsigned int max_orphans; |
188 | int err = 0; | 188 | int err = 0; |
@@ -197,10 +197,19 @@ int check_orphan_space(struct f2fs_sb_info *sbi) | |||
197 | mutex_lock(&sbi->orphan_inode_mutex); | 197 | mutex_lock(&sbi->orphan_inode_mutex); |
198 | if (sbi->n_orphans >= max_orphans) | 198 | if (sbi->n_orphans >= max_orphans) |
199 | err = -ENOSPC; | 199 | err = -ENOSPC; |
200 | else | ||
201 | sbi->n_orphans++; | ||
200 | mutex_unlock(&sbi->orphan_inode_mutex); | 202 | mutex_unlock(&sbi->orphan_inode_mutex); |
201 | return err; | 203 | return err; |
202 | } | 204 | } |
203 | 205 | ||
206 | void release_orphan_inode(struct f2fs_sb_info *sbi) | ||
207 | { | ||
208 | mutex_lock(&sbi->orphan_inode_mutex); | ||
209 | sbi->n_orphans--; | ||
210 | mutex_unlock(&sbi->orphan_inode_mutex); | ||
211 | } | ||
212 | |||
204 | void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) | 213 | void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) |
205 | { | 214 | { |
206 | struct list_head *head, *this; | 215 | struct list_head *head, *this; |
@@ -229,8 +238,6 @@ retry: | |||
229 | list_add(&new->list, this->prev); | 238 | list_add(&new->list, this->prev); |
230 | else | 239 | else |
231 | list_add_tail(&new->list, head); | 240 | list_add_tail(&new->list, head); |
232 | |||
233 | sbi->n_orphans++; | ||
234 | out: | 241 | out: |
235 | mutex_unlock(&sbi->orphan_inode_mutex); | 242 | mutex_unlock(&sbi->orphan_inode_mutex); |
236 | } | 243 | } |
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index d1bb2606b313..384c6daf9a89 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c | |||
@@ -572,6 +572,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, | |||
572 | 572 | ||
573 | if (inode->i_nlink == 0) | 573 | if (inode->i_nlink == 0) |
574 | add_orphan_inode(sbi, inode->i_ino); | 574 | add_orphan_inode(sbi, inode->i_ino); |
575 | else | ||
576 | release_orphan_inode(sbi); | ||
575 | } | 577 | } |
576 | 578 | ||
577 | if (bit_pos == NR_DENTRY_IN_BLOCK) { | 579 | if (bit_pos == NR_DENTRY_IN_BLOCK) { |
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a6858c75259d..78777cdb89de 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -1044,7 +1044,8 @@ void destroy_segment_manager(struct f2fs_sb_info *); | |||
1044 | struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); | 1044 | struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); |
1045 | struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); | 1045 | struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); |
1046 | long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); | 1046 | long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); |
1047 | int check_orphan_space(struct f2fs_sb_info *); | 1047 | int acquire_orphan_inode(struct f2fs_sb_info *); |
1048 | void release_orphan_inode(struct f2fs_sb_info *); | ||
1048 | void add_orphan_inode(struct f2fs_sb_info *, nid_t); | 1049 | void add_orphan_inode(struct f2fs_sb_info *, nid_t); |
1049 | void remove_orphan_inode(struct f2fs_sb_info *, nid_t); | 1050 | void remove_orphan_inode(struct f2fs_sb_info *, nid_t); |
1050 | int recover_orphan_inodes(struct f2fs_sb_info *); | 1051 | int recover_orphan_inodes(struct f2fs_sb_info *); |
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 32972787f12f..4e475181280c 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c | |||
@@ -239,7 +239,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) | |||
239 | if (!de) | 239 | if (!de) |
240 | goto fail; | 240 | goto fail; |
241 | 241 | ||
242 | err = check_orphan_space(sbi); | 242 | err = acquire_orphan_inode(sbi); |
243 | if (err) { | 243 | if (err) { |
244 | kunmap(page); | 244 | kunmap(page); |
245 | f2fs_put_page(page, 0); | 245 | f2fs_put_page(page, 0); |
@@ -393,7 +393,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
393 | struct inode *old_inode = old_dentry->d_inode; | 393 | struct inode *old_inode = old_dentry->d_inode; |
394 | struct inode *new_inode = new_dentry->d_inode; | 394 | struct inode *new_inode = new_dentry->d_inode; |
395 | struct page *old_dir_page; | 395 | struct page *old_dir_page; |
396 | struct page *old_page; | 396 | struct page *old_page, *new_page; |
397 | struct f2fs_dir_entry *old_dir_entry = NULL; | 397 | struct f2fs_dir_entry *old_dir_entry = NULL; |
398 | struct f2fs_dir_entry *old_entry; | 398 | struct f2fs_dir_entry *old_entry; |
399 | struct f2fs_dir_entry *new_entry; | 399 | struct f2fs_dir_entry *new_entry; |
@@ -415,7 +415,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
415 | ilock = mutex_lock_op(sbi); | 415 | ilock = mutex_lock_op(sbi); |
416 | 416 | ||
417 | if (new_inode) { | 417 | if (new_inode) { |
418 | struct page *new_page; | ||
419 | 418 | ||
420 | err = -ENOTEMPTY; | 419 | err = -ENOTEMPTY; |
421 | if (old_dir_entry && !f2fs_empty_dir(new_inode)) | 420 | if (old_dir_entry && !f2fs_empty_dir(new_inode)) |
@@ -427,9 +426,13 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
427 | if (!new_entry) | 426 | if (!new_entry) |
428 | goto out_dir; | 427 | goto out_dir; |
429 | 428 | ||
429 | err = acquire_orphan_inode(sbi); | ||
430 | if (err) | ||
431 | goto put_out_dir; | ||
432 | |||
430 | if (update_dent_inode(old_inode, &new_dentry->d_name)) { | 433 | if (update_dent_inode(old_inode, &new_dentry->d_name)) { |
431 | f2fs_put_page(new_page, 1); | 434 | release_orphan_inode(sbi); |
432 | goto out_dir; | 435 | goto put_out_dir; |
433 | } | 436 | } |
434 | 437 | ||
435 | f2fs_set_link(new_dir, new_entry, new_page, old_inode); | 438 | f2fs_set_link(new_dir, new_entry, new_page, old_inode); |
@@ -438,8 +441,12 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
438 | if (old_dir_entry) | 441 | if (old_dir_entry) |
439 | drop_nlink(new_inode); | 442 | drop_nlink(new_inode); |
440 | drop_nlink(new_inode); | 443 | drop_nlink(new_inode); |
444 | |||
441 | if (!new_inode->i_nlink) | 445 | if (!new_inode->i_nlink) |
442 | add_orphan_inode(sbi, new_inode->i_ino); | 446 | add_orphan_inode(sbi, new_inode->i_ino); |
447 | else | ||
448 | release_orphan_inode(sbi); | ||
449 | |||
443 | update_inode_page(new_inode); | 450 | update_inode_page(new_inode); |
444 | } else { | 451 | } else { |
445 | err = f2fs_add_link(new_dentry, old_inode); | 452 | err = f2fs_add_link(new_dentry, old_inode); |
@@ -472,6 +479,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
472 | mutex_unlock_op(sbi, ilock); | 479 | mutex_unlock_op(sbi, ilock); |
473 | return 0; | 480 | return 0; |
474 | 481 | ||
482 | put_out_dir: | ||
483 | f2fs_put_page(new_page, 1); | ||
475 | out_dir: | 484 | out_dir: |
476 | if (old_dir_entry) { | 485 | if (old_dir_entry) { |
477 | kunmap(old_dir_page); | 486 | kunmap(old_dir_page); |