Btrfs: Optimizations for the extent_buffer code

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index f60920e..9427b79 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -464,22 +464,12 @@
 			int level)
 {
 	struct extent_buffer *buf = path->nodes[level];
-	char fsid[BTRFS_FSID_SIZE];
 
-	read_extent_buffer(buf, fsid, (unsigned long)btrfs_header_fsid(buf),
-			   BTRFS_FSID_SIZE);
-
-	if (memcmp(fsid, root->fs_info->fsid, BTRFS_FSID_SIZE)) {
-		int i = 0;
+	if (memcmp_extent_buffer(buf, root->fs_info->fsid,
+				 (unsigned long)btrfs_header_fsid(buf),
+				 BTRFS_FSID_SIZE)) {
 		printk("warning bad block %Lu\n", buf->start);
-		if (!btrfs_buffer_uptodate(buf)) {
-			WARN_ON(1);
-		}
-		for (i = 0; i < BTRFS_FSID_SIZE; i++) {
-			printk("%x:%x ", root->fs_info->fsid[i], fsid[i]);
-		}
-		printk("\n");
-		// BUG();
+		BUG();
 	}
 	if (level == 0)
 		return check_leaf(root, path, level);
@@ -504,13 +494,14 @@
 	int high = max;
 	int mid;
 	int ret;
-	struct btrfs_disk_key *tmp;
+	struct btrfs_disk_key *tmp = NULL;
 	struct btrfs_disk_key unaligned;
 	unsigned long offset;
 	char *map_token = NULL;
 	char *kaddr = NULL;
 	unsigned long map_start = 0;
 	unsigned long map_len = 0;
+	int err;
 
 	while(low < high) {
 		mid = (low + high) / 2;
@@ -519,19 +510,24 @@
 		if (!map_token || offset < map_start ||
 		    (offset + sizeof(struct btrfs_disk_key)) >
 		    map_start + map_len) {
-			if (map_token)
+			if (map_token) {
 				unmap_extent_buffer(eb, map_token, KM_USER0);
-			map_extent_buffer(eb, offset, &map_token, &kaddr,
-					  &map_start, &map_len, KM_USER0);
+				map_token = NULL;
+			}
+			err = map_extent_buffer(eb, offset,
+						sizeof(struct btrfs_disk_key),
+						&map_token, &kaddr,
+						&map_start, &map_len, KM_USER0);
 
-		}
-		if (offset + sizeof(struct btrfs_disk_key) >
-		    map_start + map_len) {
-			unmap_extent_buffer(eb, map_token, KM_USER0);
-			read_extent_buffer(eb, &unaligned,
-					   offset, sizeof(unaligned));
-			map_token = NULL;
-			tmp = &unaligned;
+			if (!err) {
+				tmp = (struct btrfs_disk_key *)(kaddr + offset -
+							map_start);
+			} else {
+				read_extent_buffer(eb, &unaligned,
+						   offset, sizeof(unaligned));
+				tmp = &unaligned;
+			}
+
 		} else {
 			tmp = (struct btrfs_disk_key *)(kaddr + offset -
 							map_start);
@@ -544,7 +540,8 @@
 			high = mid;
 		else {
 			*slot = mid;
-			unmap_extent_buffer(eb, map_token, KM_USER0);
+			if (map_token)
+				unmap_extent_buffer(eb, map_token, KM_USER0);
 			return 0;
 		}
 	}
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index c4b8298..30fbbd7 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -22,6 +22,7 @@
 #include <linux/fs.h>
 #include <linux/workqueue.h>
 #include <linux/completion.h>
+#include <asm/kmap_types.h>
 #include "bit-radix.h"
 #include "extent_map.h"
 
@@ -431,15 +432,52 @@
 static inline u##bits btrfs_##name(struct extent_buffer *eb,		\
 				   type *s)				\
 {									\
-	__le##bits res;							\
-	read_eb_member(eb, s, type, member, &res);			\
-	return le##bits##_to_cpu(res);					\
+	int err;							\
+	char *map_token;						\
+	char *kaddr;							\
+	unsigned long map_start;					\
+	unsigned long map_len;						\
+	unsigned long offset = (unsigned long)s +			\
+				offsetof(type, member);			\
+	err = map_extent_buffer(eb, offset,				\
+			        sizeof(((type *)0)->member),		\
+				&map_token, &kaddr,			\
+				&map_start, &map_len, KM_USER0);	\
+	if (!err) {							\
+		__le##bits *tmp = (__le##bits *)(kaddr + offset -	\
+					       map_start);		\
+		u##bits res = le##bits##_to_cpu(*tmp);			\
+		unmap_extent_buffer(eb, map_token, KM_USER0);		\
+		return res;						\
+	} else {							\
+		__le##bits res;						\
+		read_eb_member(eb, s, type, member, &res);		\
+		return le##bits##_to_cpu(res);				\
+	}								\
 }									\
 static inline void btrfs_set_##name(struct extent_buffer *eb,		\
 				    type *s, u##bits val)		\
 {									\
-	val = cpu_to_le##bits(val);					\
-	write_eb_member(eb, s, type, member, &val);			\
+	int err;							\
+	char *map_token;						\
+	char *kaddr;							\
+	unsigned long map_start;					\
+	unsigned long map_len;						\
+	unsigned long offset = (unsigned long)s +			\
+				offsetof(type, member);			\
+	err = map_extent_buffer(eb, offset,				\
+			        sizeof(((type *)0)->member),		\
+				&map_token, &kaddr,			\
+				&map_start, &map_len, KM_USER0);	\
+	if (!err) {							\
+		__le##bits *tmp = (__le##bits *)(kaddr + offset -	\
+					       map_start);		\
+		*tmp = cpu_to_le##bits(val);				\
+		unmap_extent_buffer(eb, map_token, KM_USER0);		\
+	} else {							\
+		val = cpu_to_le##bits(val);				\
+		write_eb_member(eb, s, type, member, &val);		\
+	}								\
 }
 
 #define BTRFS_SETGET_HEADER_FUNCS(name, type, member, bits)		\
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 55fefdf..8bef309 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -2140,14 +2140,20 @@
 EXPORT_SYMBOL(read_extent_buffer);
 
 int map_extent_buffer(struct extent_buffer *eb, unsigned long start,
+		      unsigned long min_len,
 		      char **token, char **map,
 		      unsigned long *map_start,
 		      unsigned long *map_len, int km)
 {
-	size_t offset;
+	size_t offset = start & (PAGE_CACHE_SIZE - 1);
 	char *kaddr;
 	size_t start_offset = eb->start & ((u64)PAGE_CACHE_SIZE - 1);
 	unsigned long i = (start_offset + start) >> PAGE_CACHE_SHIFT;
+	unsigned long end_i = (start_offset + start + min_len) >>
+				PAGE_CACHE_SHIFT;
+
+	if (i != end_i)
+		return -EINVAL;
 
 	WARN_ON(start > eb->len);
 
@@ -2155,8 +2161,7 @@
 		offset = start_offset;
 		*map_start = 0;
 	} else {
-		offset = 0;
-		*map_start = (i << PAGE_CACHE_SHIFT) - offset;
+		*map_start = (i << PAGE_CACHE_SHIFT) - start_offset;
 	}
 
 	// kaddr = kmap_atomic(eb->pages[i], km);
diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
index 430b997..8957890 100644
--- a/fs/btrfs/extent_map.h
+++ b/fs/btrfs/extent_map.h
@@ -173,7 +173,7 @@
 int extent_buffer_uptodate(struct extent_map_tree *tree,
 			   struct extent_buffer *eb);
 int map_extent_buffer(struct extent_buffer *eb, unsigned long offset,
-		      char **token, char **map,
+		      unsigned long min_len, char **token, char **map,
 		      unsigned long *map_start,
 		      unsigned long *map_len, int km);
 void unmap_extent_buffer(struct extent_buffer *eb, char *token, int km);