]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
f2fs: fix handling orphan inodes
authorJaegeuk Kim <jaegeuk.kim@samsung.com>
Tue, 30 Jul 2013 02:36:53 +0000 (11:36 +0900)
committerJaegeuk Kim <jaegeuk.kim@samsung.com>
Tue, 30 Jul 2013 06:17:03 +0000 (15:17 +0900)
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>
fs/f2fs/checkpoint.c
fs/f2fs/dir.c
fs/f2fs/f2fs.h
fs/f2fs/namei.c

index fe91773de130200f665152248b4cab43cf5ec879..c5a5c390a5cc6a91dc524de04f10526ff55674c1 100644 (file)
@@ -182,7 +182,7 @@ const struct address_space_operations f2fs_meta_aops = {
        .set_page_dirty = f2fs_set_meta_page_dirty,
 };
 
-int check_orphan_space(struct f2fs_sb_info *sbi)
+int acquire_orphan_inode(struct f2fs_sb_info *sbi)
 {
        unsigned int max_orphans;
        int err = 0;
@@ -197,10 +197,19 @@ int check_orphan_space(struct f2fs_sb_info *sbi)
        mutex_lock(&sbi->orphan_inode_mutex);
        if (sbi->n_orphans >= max_orphans)
                err = -ENOSPC;
+       else
+               sbi->n_orphans++;
        mutex_unlock(&sbi->orphan_inode_mutex);
        return err;
 }
 
+void release_orphan_inode(struct f2fs_sb_info *sbi)
+{
+       mutex_lock(&sbi->orphan_inode_mutex);
+       sbi->n_orphans--;
+       mutex_unlock(&sbi->orphan_inode_mutex);
+}
+
 void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
 {
        struct list_head *head, *this;
@@ -229,8 +238,6 @@ retry:
                list_add(&new->list, this->prev);
        else
                list_add_tail(&new->list, head);
-
-       sbi->n_orphans++;
 out:
        mutex_unlock(&sbi->orphan_inode_mutex);
 }
index d1bb2606b3133183f67e44f4c560fe3e080b8c8d..384c6daf9a89a668b25bf2a22a193ac9f23f6a8a 100644 (file)
@@ -572,6 +572,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
 
                if (inode->i_nlink == 0)
                        add_orphan_inode(sbi, inode->i_ino);
+               else
+                       release_orphan_inode(sbi);
        }
 
        if (bit_pos == NR_DENTRY_IN_BLOCK) {
index a6858c75259d632a3478e821180efc0ba6282c96..78777cdb89de3581e98839cc250acc095ce6752b 100644 (file)
@@ -1044,7 +1044,8 @@ void destroy_segment_manager(struct f2fs_sb_info *);
 struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
 struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t);
 long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
-int check_orphan_space(struct f2fs_sb_info *);
+int acquire_orphan_inode(struct f2fs_sb_info *);
+void release_orphan_inode(struct f2fs_sb_info *);
 void add_orphan_inode(struct f2fs_sb_info *, nid_t);
 void remove_orphan_inode(struct f2fs_sb_info *, nid_t);
 int recover_orphan_inodes(struct f2fs_sb_info *);
index 32972787f12f4905dd0d696151e3c07165d124e9..4e475181280c4a8865513f89297a17ca14af07f0 100644 (file)
@@ -239,7 +239,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
        if (!de)
                goto fail;
 
-       err = check_orphan_space(sbi);
+       err = acquire_orphan_inode(sbi);
        if (err) {
                kunmap(page);
                f2fs_put_page(page, 0);
@@ -393,7 +393,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct inode *old_inode = old_dentry->d_inode;
        struct inode *new_inode = new_dentry->d_inode;
        struct page *old_dir_page;
-       struct page *old_page;
+       struct page *old_page, *new_page;
        struct f2fs_dir_entry *old_dir_entry = NULL;
        struct f2fs_dir_entry *old_entry;
        struct f2fs_dir_entry *new_entry;
@@ -415,7 +415,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
        ilock = mutex_lock_op(sbi);
 
        if (new_inode) {
-               struct page *new_page;
 
                err = -ENOTEMPTY;
                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,
                if (!new_entry)
                        goto out_dir;
 
+               err = acquire_orphan_inode(sbi);
+               if (err)
+                       goto put_out_dir;
+
                if (update_dent_inode(old_inode, &new_dentry->d_name)) {
-                       f2fs_put_page(new_page, 1);
-                       goto out_dir;
+                       release_orphan_inode(sbi);
+                       goto put_out_dir;
                }
 
                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,
                if (old_dir_entry)
                        drop_nlink(new_inode);
                drop_nlink(new_inode);
+
                if (!new_inode->i_nlink)
                        add_orphan_inode(sbi, new_inode->i_ino);
+               else
+                       release_orphan_inode(sbi);
+
                update_inode_page(new_inode);
        } else {
                err = f2fs_add_link(new_dentry, old_inode);
@@ -472,6 +479,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
        mutex_unlock_op(sbi, ilock);
        return 0;
 
+put_out_dir:
+       f2fs_put_page(new_page, 1);
 out_dir:
        if (old_dir_entry) {
                kunmap(old_dir_page);