Btrfs: While doing checksums on bios, cache the extent_buffer mapping

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 3f0e71b..ee25e50 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -156,6 +156,11 @@
 	int bio_index = 0;
 	struct bio_vec *bvec = bio->bi_io_vec;
 	char *data;
+	char *eb_map;
+	char *eb_token;
+	unsigned long map_len;
+	unsigned long map_start;
+
 
 	path = btrfs_alloc_path();
 	BUG_ON(!path);
@@ -272,6 +277,7 @@
 	item_end = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
 	item_end = (struct btrfs_csum_item *)((unsigned char *)item_end +
 				      btrfs_item_size_nr(leaf, path->slots[0]));
+	eb_token = NULL;
 next_bvec:
 	data = kmap_atomic(bvec->bv_page, KM_IRQ0);
 	csum_result = ~(u32)0;
@@ -283,15 +289,39 @@
 		printk("csum result is 0 for inode %lu offset %Lu\n", inode->i_ino, offset);
 	}
 
-	write_extent_buffer(leaf, &csum_result, (unsigned long)item,
-			    BTRFS_CRC32_SIZE);
+	if (!eb_token ||
+	   (unsigned long)item  + BTRFS_CRC32_SIZE >= map_start + map_len) {
+		int err;
+
+		if (eb_token)
+			unmap_extent_buffer(leaf, eb_token, KM_IRQ1);
+		eb_token = NULL;
+		err = map_private_extent_buffer(leaf, (unsigned long)item,
+						BTRFS_CRC32_SIZE,
+						&eb_token, &eb_map,
+						&map_start, &map_len, KM_IRQ1);
+		if (err)
+			eb_token = NULL;
+	}
+	if (eb_token) {
+		memcpy(eb_token + ((unsigned long)item & (PAGE_CACHE_SIZE - 1)),
+		       &csum_result, BTRFS_CRC32_SIZE);
+	} else {
+		write_extent_buffer(leaf, &csum_result, (unsigned long)item,
+				    BTRFS_CRC32_SIZE);
+	}
 	bio_index++;
 	bvec++;
 	if (bio_index < bio->bi_vcnt) {
-		item = (struct btrfs_csum_item *)((char *)item + BTRFS_CRC32_SIZE);
+		item = (struct btrfs_csum_item *)((char *)item +
+						  BTRFS_CRC32_SIZE);
 		if (item < item_end)
 			goto next_bvec;
 	}
+	if (eb_token) {
+		unmap_extent_buffer(leaf, eb_token, KM_IRQ1);
+		eb_token = NULL;
+	}
 	btrfs_mark_buffer_dirty(path->nodes[0]);
 	if (bio_index < bio->bi_vcnt) {
 		btrfs_release_path(root, path);