Btrfs: Handle data block end_io through the async work queue

Before it was done by the bio end_io routine, the work queue code is able
to scale much better with faster IO subsystems.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 8210920..94ff87d 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -54,6 +54,7 @@
 	void *private;
 	struct btrfs_fs_info *info;
 	int error;
+	int metadata;
 	struct list_head list;
 };
 
@@ -308,29 +309,40 @@
 #endif
 }
 
-static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio)
+int btrfs_bio_wq_end_io(struct btrfs_fs_info *info, struct bio *bio,
+			int metadata)
 {
-	struct btrfs_root *root = BTRFS_I(inode)->root;
 	struct end_io_wq *end_io_wq;
-	u64 offset;
-	offset = bio->bi_sector << 9;
-
-	if (rw & (1 << BIO_RW)) {
-		return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio);
-	}
-
 	end_io_wq = kmalloc(sizeof(*end_io_wq), GFP_NOFS);
 	if (!end_io_wq)
 		return -ENOMEM;
 
 	end_io_wq->private = bio->bi_private;
 	end_io_wq->end_io = bio->bi_end_io;
-	end_io_wq->info = root->fs_info;
+	end_io_wq->info = info;
 	end_io_wq->error = 0;
 	end_io_wq->bio = bio;
+	end_io_wq->metadata = metadata;
 
 	bio->bi_private = end_io_wq;
 	bio->bi_end_io = end_workqueue_bio;
+	return 0;
+}
+
+static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio)
+{
+	struct btrfs_root *root = BTRFS_I(inode)->root;
+	u64 offset;
+	int ret;
+
+	offset = bio->bi_sector << 9;
+
+	if (rw & (1 << BIO_RW)) {
+		return btrfs_map_bio(BTRFS_I(inode)->root, rw, bio);
+	}
+
+	ret = btrfs_bio_wq_end_io(root->fs_info, bio, 1);
+	BUG_ON(ret);
 
 	if (offset == BTRFS_SUPER_INFO_OFFSET) {
 		bio->bi_bdev = root->fs_info->sb->s_bdev;
@@ -880,7 +892,7 @@
 		end_io_wq = list_entry(next, struct end_io_wq, list);
 
 		bio = end_io_wq->bio;
-		if (!bio_ready_for_csum(bio)) {
+		if (end_io_wq->metadata && !bio_ready_for_csum(bio)) {
 			spin_lock_irqsave(&fs_info->end_io_work_lock, flags);
 			was_empty = list_empty(&fs_info->end_io_work_list);
 			list_add_tail(&end_io_wq->list,
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 05b88d0..4fac0cc 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -71,4 +71,6 @@
 int btrfs_open_device(struct btrfs_device *dev);
 int btrfs_verify_block_csum(struct btrfs_root *root,
 			    struct extent_buffer *buf);
+int btrfs_bio_wq_end_io(struct btrfs_fs_info *info, struct bio *bio,
+			int metadata);
 #endif
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 7ae677d..e1ef1ac 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -328,7 +328,9 @@
 	struct btrfs_trans_handle *trans;
 	int ret = 0;
 
-	if (rw != WRITE) {
+	if (!(rw & (1 << BIO_RW))) {
+		ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
+		BUG_ON(ret);
 		goto mapit;
 	}