Btrfs: Add a write ahead tree log to optimize synchronous operations

File syncs and directory syncs are optimized by copying their
items into a special (copy-on-write) log tree.  There is one log tree per
subvolume and the btrfs super block points to a tree of log tree roots.

After a crash, items are copied out of the log tree and back into the
subvolume.  See tree-log.c for all the details.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index e9e86fb..84ecf3a 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -36,6 +36,8 @@
 #include "btrfs_inode.h"
 #include "ioctl.h"
 #include "print-tree.h"
+#include "tree-log.h"
+#include "locking.h"
 #include "compat.h"
 
 
@@ -988,10 +990,27 @@
 	*ppos = pos;
 
 	if (num_written > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
-		err = sync_page_range(inode, inode->i_mapping,
-				      start_pos, num_written);
+		struct btrfs_trans_handle *trans;
+
+		err = btrfs_fdatawrite_range(inode->i_mapping, start_pos,
+					     start_pos + num_written -1,
+					     WB_SYNC_NONE);
 		if (err < 0)
 			num_written = err;
+
+		err = btrfs_wait_on_page_writeback_range(inode->i_mapping,
+				 start_pos, start_pos + num_written - 1);
+		if (err < 0)
+			num_written = err;
+
+		trans = btrfs_start_transaction(root, 1);
+		ret = btrfs_log_dentry_safe(trans, root, file->f_dentry);
+		if (ret == 0) {
+			btrfs_sync_log(trans, root);
+			btrfs_end_transaction(trans, root);
+		} else {
+			btrfs_commit_transaction(trans, root);
+		}
 	} else if (num_written > 0 && (file->f_flags & O_DIRECT)) {
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
 		do_sync_file_range(file, start_pos,
@@ -1019,8 +1038,7 @@
 	return 0;
 }
 
-static int btrfs_sync_file(struct file *file,
-			   struct dentry *dentry, int datasync)
+int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
 {
 	struct inode *inode = dentry->d_inode;
 	struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -1043,6 +1061,8 @@
 	}
 	mutex_unlock(&root->fs_info->trans_mutex);
 
+	filemap_fdatawait(inode->i_mapping);
+
 	/*
 	 * ok we haven't committed the transaction yet, lets do a commit
 	 */
@@ -1054,7 +1074,16 @@
 		ret = -ENOMEM;
 		goto out;
 	}
-	ret = btrfs_commit_transaction(trans, root);
+
+	ret = btrfs_log_dentry_safe(trans, root, file->f_dentry);
+	if (ret < 0)
+		goto out;
+	if (ret > 0) {
+		ret = btrfs_commit_transaction(trans, root);
+	} else {
+		btrfs_sync_log(trans, root);
+		ret = btrfs_end_transaction(trans, root);
+	}
 out:
 	return ret > 0 ? EIO : ret;
 }