Btrfs: Dir fsync optimizations
authorChris Mason <chris.mason@oracle.com>
Thu, 11 Sep 2008 19:53:12 +0000 (15:53 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 25 Sep 2008 15:04:07 +0000 (11:04 -0400)
Drop i_mutex during the commit

Don't bother doing the fsync at all unless the dir is marked as dirtied
and needing fsync in this transaction.  For directories, this means
that someone has unlinked a file from the dir without fsyncing the
file.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/btrfs_inode.h
fs/btrfs/file.c
fs/btrfs/inode.c
fs/btrfs/tree-log.c

index fcc8cf27e906c8d865ecb08f8e353ae181656207..0577fda2168aa9712b02c7b392681e81dde11d85 100644 (file)
@@ -56,6 +56,9 @@ struct btrfs_inode {
         * transid that last logged this inode
         */
        u64 logged_trans;
+
+       /* trans that last made a change that should be fully fsync'd */
+       u64 log_dirty_trans;
        u64 delalloc_bytes;
        u64 disk_i_size;
        u32 flags;
index 84ecf3ab8511e04423cf44fc2811b6608bacd922..58b329ddb426d2063e6a73b4eb50660ae04e77b5 100644 (file)
@@ -1061,7 +1061,9 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
        }
        mutex_unlock(&root->fs_info->trans_mutex);
 
+       root->fs_info->tree_log_batch++;
        filemap_fdatawait(inode->i_mapping);
+       root->fs_info->tree_log_batch++;
 
        /*
         * ok we haven't committed the transaction yet, lets do a commit
@@ -1076,14 +1078,29 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
        }
 
        ret = btrfs_log_dentry_safe(trans, root, file->f_dentry);
-       if (ret < 0)
+       if (ret < 0) {
                goto out;
+       }
+
+       /* we've logged all the items and now have a consistent
+        * version of the file in the log.  It is possible that
+        * someone will come in and modify the file, but that's
+        * fine because the log is consistent on disk, and we
+        * have references to all of the file's extents
+        *
+        * It is possible that someone will come in and log the
+        * file again, but that will end up using the synchronization
+        * inside btrfs_sync_log to keep things safe.
+        */
+       mutex_unlock(&file->f_dentry->d_inode->i_mutex);
+
        if (ret > 0) {
                ret = btrfs_commit_transaction(trans, root);
        } else {
                btrfs_sync_log(trans, root);
                ret = btrfs_end_transaction(trans, root);
        }
+       mutex_lock(&file->f_dentry->d_inode->i_mutex);
 out:
        return ret > 0 ? EIO : ret;
 }
index 24b7e97fccb9145a1d4d22876d9a89f23f4b8de3..12c1c0530f3deac96f17c693add551e9c1e3b80f 100644 (file)
@@ -1187,7 +1187,9 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
 
        ret = btrfs_del_inode_ref_in_log(trans, root, name, name_len,
                                         inode, dir->i_ino);
-       BUG_ON(ret);
+       BUG_ON(ret != 0 && ret != -ENOENT);
+       if (ret != -ENOENT)
+               BTRFS_I(dir)->log_dirty_trans = trans->transid;
 
        ret = btrfs_del_dir_entries_in_log(trans, root, name, name_len,
                                           dir, index);
@@ -1790,6 +1792,7 @@ static noinline void init_btrfs_i(struct inode *inode)
        bi->disk_i_size = 0;
        bi->flags = 0;
        bi->index_cnt = (u64)-1;
+       bi->log_dirty_trans = 0;
        extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
        extent_io_tree_init(&BTRFS_I(inode)->io_tree,
                             inode->i_mapping, GFP_NOFS);
index 3f4b139b27ed5564450e28516e161f628534c3f7..5d49a701bdcddb42d7277a204962b911ed942200 100644 (file)
@@ -1973,10 +1973,10 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
        atomic_set(&log->fs_info->tree_log_commit, 1);
 
        while(1) {
+               batch = log->fs_info->tree_log_batch;
                mutex_unlock(&log->fs_info->tree_log_mutex);
                schedule_timeout_uninterruptible(1);
                mutex_lock(&log->fs_info->tree_log_mutex);
-               batch = log->fs_info->tree_log_batch;
 
                while(atomic_read(&log->fs_info->tree_log_writers)) {
                        DEFINE_WAIT(wait);
@@ -2189,8 +2189,6 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
        mutex_unlock(&BTRFS_I(inode)->log_mutex);
        end_log_trans(root);
 
-       if (ret == 0 || ret == -ENOENT)
-               return 0;
        return ret;
 }
 
@@ -2620,9 +2618,11 @@ static int __btrfs_log_inode(struct btrfs_trans_handle *trans,
                else
                        break;
        }
-       if (inode_only == LOG_INODE_ALL && S_ISDIR(inode->i_mode)) {
+       if (inode_only == LOG_INODE_ALL && S_ISDIR(inode->i_mode) &&
+           BTRFS_I(inode)->log_dirty_trans >= trans->transid) {
                btrfs_release_path(root, path);
                btrfs_release_path(log, dst_path);
+               BTRFS_I(inode)->log_dirty_trans = 0;
                ret = log_directory_changes(trans, root, inode, path, dst_path);
                BUG_ON(ret);
        }