aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaegeuk Kim <jaegeuk.kim@samsung.com>2013-07-29 22:36:53 -0400
committerJaegeuk Kim <jaegeuk.kim@samsung.com>2013-07-30 02:17:03 -0400
commitcbd56e7d20d7188d62a85aa6986a7b2c8e755ab5 (patch)
tree44962739f9ce15214fc5f0587b2174aae1f032f5
parentd8207f69589c74037128ff6c9e1a44223fad3b7c (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.c13
-rw-r--r--fs/f2fs/dir.c2
-rw-r--r--fs/f2fs/f2fs.h3
-rw-r--r--fs/f2fs/namei.c19
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
185int check_orphan_space(struct f2fs_sb_info *sbi) 185int 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
206void 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
204void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) 213void 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++;
234out: 241out:
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 *);
1044struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); 1044struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
1045struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); 1045struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t);
1046long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); 1046long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
1047int check_orphan_space(struct f2fs_sb_info *); 1047int acquire_orphan_inode(struct f2fs_sb_info *);
1048void release_orphan_inode(struct f2fs_sb_info *);
1048void add_orphan_inode(struct f2fs_sb_info *, nid_t); 1049void add_orphan_inode(struct f2fs_sb_info *, nid_t);
1049void remove_orphan_inode(struct f2fs_sb_info *, nid_t); 1050void remove_orphan_inode(struct f2fs_sb_info *, nid_t);
1050int recover_orphan_inodes(struct f2fs_sb_info *); 1051int 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
482put_out_dir:
483 f2fs_put_page(new_page, 1);
475out_dir: 484out_dir:
476 if (old_dir_entry) { 485 if (old_dir_entry) {
477 kunmap(old_dir_page); 486 kunmap(old_dir_page);