Fix reiserfs_file_release()
authorAl Viro <viro@zeniv.linux.org.uk>
Sun, 4 Jul 2010 08:18:57 +0000 (12:18 +0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 9 Aug 2010 20:47:27 +0000 (16:47 -0400)
a) count file openers correctly; i_count use was completely wrong
b) use new mutex for exclusion between final close/open/truncate,
to protect tailpacking logics.  i_mutex use was wrong and resulted
in deadlocks.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/reiserfs/file.c
fs/reiserfs/inode.c
fs/reiserfs/super.c
include/linux/reiserfs_fs_i.h

index b82cdd8a45dd88c593f06f67ec8f88515d8b500b..6846371498b685becc1ff8eba16b2e62933c57d5 100644 (file)
@@ -38,20 +38,24 @@ static int reiserfs_file_release(struct inode *inode, struct file *filp)
 
        BUG_ON(!S_ISREG(inode->i_mode));
 
+        if (atomic_add_unless(&REISERFS_I(inode)->openers, -1, 1))
+               return 0;
+
+       mutex_lock(&(REISERFS_I(inode)->tailpack));
+
+        if (!atomic_dec_and_test(&REISERFS_I(inode)->openers)) {
+               mutex_unlock(&(REISERFS_I(inode)->tailpack));
+               return 0;
+       }
+
        /* fast out for when nothing needs to be done */
-       if ((atomic_read(&inode->i_count) > 1 ||
-            !(REISERFS_I(inode)->i_flags & i_pack_on_close_mask) ||
+       if ((!(REISERFS_I(inode)->i_flags & i_pack_on_close_mask) ||
             !tail_has_to_be_packed(inode)) &&
            REISERFS_I(inode)->i_prealloc_count <= 0) {
+               mutex_unlock(&(REISERFS_I(inode)->tailpack));
                return 0;
        }
 
-       mutex_lock(&inode->i_mutex);
-
-       mutex_lock(&(REISERFS_I(inode)->i_mmap));
-       if (REISERFS_I(inode)->i_flags & i_ever_mapped)
-               REISERFS_I(inode)->i_flags &= ~i_pack_on_close_mask;
-
        reiserfs_write_lock(inode->i_sb);
        /* freeing preallocation only involves relogging blocks that
         * are already in the current transaction.  preallocation gets
@@ -94,9 +98,10 @@ static int reiserfs_file_release(struct inode *inode, struct file *filp)
        if (!err)
                err = jbegin_failure;
 
-       if (!err && atomic_read(&inode->i_count) <= 1 &&
+       if (!err &&
            (REISERFS_I(inode)->i_flags & i_pack_on_close_mask) &&
            tail_has_to_be_packed(inode)) {
+
                /* if regular file is released by last holder and it has been
                   appended (we append by unformatted node only) or its direct
                   item(s) had to be converted, then it may have to be
@@ -104,27 +109,28 @@ static int reiserfs_file_release(struct inode *inode, struct file *filp)
                err = reiserfs_truncate_file(inode, 0);
        }
       out:
-       mutex_unlock(&(REISERFS_I(inode)->i_mmap));
-       mutex_unlock(&inode->i_mutex);
        reiserfs_write_unlock(inode->i_sb);
+       mutex_unlock(&(REISERFS_I(inode)->tailpack));
        return err;
 }
 
-static int reiserfs_file_mmap(struct file *file, struct vm_area_struct *vma)
+static int reiserfs_file_open(struct inode *inode, struct file *file)
 {
-       struct inode *inode;
-
-       inode = file->f_path.dentry->d_inode;
-       mutex_lock(&(REISERFS_I(inode)->i_mmap));
-       REISERFS_I(inode)->i_flags |= i_ever_mapped;
-       mutex_unlock(&(REISERFS_I(inode)->i_mmap));
-
-       return generic_file_mmap(file, vma);
+       int err = dquot_file_open(inode, file);
+        if (!atomic_inc_not_zero(&REISERFS_I(inode)->openers)) {
+               /* somebody might be tailpacking on final close; wait for it */
+               mutex_lock(&(REISERFS_I(inode)->tailpack));
+               atomic_inc(&REISERFS_I(inode)->openers);
+               mutex_unlock(&(REISERFS_I(inode)->tailpack));
+       }
+       return err;
 }
 
 static void reiserfs_vfs_truncate_file(struct inode *inode)
 {
+       mutex_lock(&(REISERFS_I(inode)->tailpack));
        reiserfs_truncate_file(inode, 1);
+       mutex_unlock(&(REISERFS_I(inode)->tailpack));
 }
 
 /* Sync a reiserfs file. */
@@ -288,8 +294,8 @@ const struct file_operations reiserfs_file_operations = {
 #ifdef CONFIG_COMPAT
        .compat_ioctl = reiserfs_compat_ioctl,
 #endif
-       .mmap = reiserfs_file_mmap,
-       .open = dquot_file_open,
+       .mmap = generic_file_mmap,
+       .open = reiserfs_file_open,
        .release = reiserfs_file_release,
        .fsync = reiserfs_sync_file,
        .aio_read = generic_file_aio_read,
index 0f22fdaf54ac037ecc21dea052d135d340a5d510..6edac85c2b9311dbebdd32fbf0d647685646730e 100644 (file)
@@ -1138,7 +1138,6 @@ static void init_inode(struct inode *inode, struct treepath *path)
        REISERFS_I(inode)->i_prealloc_count = 0;
        REISERFS_I(inode)->i_trans_id = 0;
        REISERFS_I(inode)->i_jl = NULL;
-       mutex_init(&(REISERFS_I(inode)->i_mmap));
        reiserfs_init_xattr_rwsem(inode);
 
        if (stat_data_v1(ih)) {
@@ -1841,7 +1840,6 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th,
        REISERFS_I(inode)->i_attrs =
            REISERFS_I(dir)->i_attrs & REISERFS_INHERIT_MASK;
        sd_attrs_to_i_attrs(REISERFS_I(inode)->i_attrs, inode);
-       mutex_init(&(REISERFS_I(inode)->i_mmap));
        reiserfs_init_xattr_rwsem(inode);
 
        /* key to search for correct place for new stat data */
index 9822fa15118ba52c743ec38c7def170a58c13a0a..1e1ee9056eb6a6e8765ef354f427e638239937b1 100644 (file)
@@ -525,6 +525,8 @@ static struct inode *reiserfs_alloc_inode(struct super_block *sb)
            kmem_cache_alloc(reiserfs_inode_cachep, GFP_KERNEL);
        if (!ei)
                return NULL;
+       atomic_set(&ei->openers, 0);
+       mutex_init(&ei->tailpack);
        return &ei->vfs_inode;
 }
 
index 89f4d3abbf5af501610edeac780448e94cc4de55..97959bdfe214231018cd5182248dbe313eb1538b 100644 (file)
@@ -25,7 +25,6 @@ typedef enum {
        i_link_saved_truncate_mask = 0x0020,
        i_has_xattr_dir = 0x0040,
        i_data_log = 0x0080,
-       i_ever_mapped = 0x0100
 } reiserfs_inode_flags;
 
 struct reiserfs_inode_info {
@@ -53,7 +52,8 @@ struct reiserfs_inode_info {
         ** flushed */
        unsigned int i_trans_id;
        struct reiserfs_journal_list *i_jl;
-       struct mutex i_mmap;
+       atomic_t openers;
+       struct mutex tailpack;
 #ifdef CONFIG_REISERFS_FS_XATTR
        struct rw_semaphore i_xattr_sem;
 #endif