Reorder the flags field in struct btrfs_header and record a flag on writeout

This allows detection of blocks that have already been written in the
running transaction so they can be recowed instead of modified again.
It is step one in trusting the transid field of the block pointers.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 70b6ddf..6ba5394 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -124,6 +124,7 @@
 	btrfs_set_header_bytenr(cow, cow->start);
 	btrfs_set_header_generation(cow, trans->transid);
 	btrfs_set_header_owner(cow, new_root_objectid);
+	btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN);
 
 	WARN_ON(btrfs_header_generation(buf) > trans->transid);
 	ret = btrfs_inc_ref(trans, new_root, buf);
@@ -183,6 +184,7 @@
 	btrfs_set_header_bytenr(cow, cow->start);
 	btrfs_set_header_generation(cow, trans->transid);
 	btrfs_set_header_owner(cow, root->root_key.objectid);
+	btrfs_clear_header_flag(cow, BTRFS_HEADER_FLAG_WRITTEN);
 
 	WARN_ON(btrfs_header_generation(buf) > trans->transid);
 	if (btrfs_header_generation(buf) != trans->transid) {
@@ -245,11 +247,14 @@
 	}
 
 	header_trans = btrfs_header_generation(buf);
-	if (header_trans == trans->transid) {
+	spin_lock(&root->fs_info->hash_lock);
+	if (header_trans == trans->transid &&
+	    !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) {
 		*cow_ret = buf;
+		spin_unlock(&root->fs_info->hash_lock);
 		return 0;
 	}
-
+	spin_unlock(&root->fs_info->hash_lock);
 	search_start = buf->start & ~((u64)(1024 * 1024 * 1024) - 1);
 	ret = __btrfs_cow_block(trans, root, buf, parent,
 				 parent_slot, cow_ret, search_start, 0);
@@ -1494,6 +1499,7 @@
 	btrfs_set_header_bytenr(split, split->start);
 	btrfs_set_header_generation(split, trans->transid);
 	btrfs_set_header_owner(split, root->root_key.objectid);
+	btrfs_set_header_flags(split, 0);
 	write_extent_buffer(split, root->fs_info->fsid,
 			    (unsigned long)btrfs_header_fsid(split),
 			    BTRFS_FSID_SIZE);
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index fec96ba..67d533c 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -193,6 +193,8 @@
 }
 
 #define BTRFS_FSID_SIZE 16
+#define BTRFS_HEADER_FLAG_WRITTEN (1 << 0)
+
 /*
  * every tree block (leaf or node) starts with this header.
  */
@@ -200,10 +202,10 @@
 	u8 csum[BTRFS_CSUM_SIZE];
 	u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
 	__le64 bytenr; /* which block this node is supposed to live in */
+	__le64 flags;
 	__le64 generation;
 	__le64 owner;
 	__le32 nritems;
-	__le16 flags;
 	u8 level;
 } __attribute__ ((__packed__));
 
@@ -229,9 +231,10 @@
  */
 struct btrfs_super_block {
 	u8 csum[BTRFS_CSUM_SIZE];
-	/* the first 3 fields must match struct btrfs_header */
+	/* the first 4 fields must match struct btrfs_header */
 	u8 fsid[16];    /* FS specific uuid */
 	__le64 bytenr; /* this block number */
+	__le64 flags;
 	__le64 magic;
 	__le64 generation;
 	__le64 root;
@@ -1045,9 +1048,28 @@
 			  generation, 64);
 BTRFS_SETGET_HEADER_FUNCS(header_owner, struct btrfs_header, owner, 64);
 BTRFS_SETGET_HEADER_FUNCS(header_nritems, struct btrfs_header, nritems, 32);
-BTRFS_SETGET_HEADER_FUNCS(header_flags, struct btrfs_header, flags, 16);
+BTRFS_SETGET_HEADER_FUNCS(header_flags, struct btrfs_header, flags, 64);
 BTRFS_SETGET_HEADER_FUNCS(header_level, struct btrfs_header, level, 8);
 
+static inline int btrfs_header_flag(struct extent_buffer *eb, u64 flag)
+{
+	return (btrfs_header_flags(eb) & flag) == flag;
+}
+
+static inline int btrfs_set_header_flag(struct extent_buffer *eb, u64 flag)
+{
+	u64 flags = btrfs_header_flags(eb);
+	btrfs_set_header_flags(eb, flags | flag);
+	return (flags & flag) == flag;
+}
+
+static inline int btrfs_clear_header_flag(struct extent_buffer *eb, u64 flag)
+{
+	u64 flags = btrfs_header_flags(eb);
+	btrfs_set_header_flags(eb, flags & ~flag);
+	return (flags & flag) == flag;
+}
+
 static inline u8 *btrfs_header_fsid(struct extent_buffer *eb)
 {
 	unsigned long ptr = offsetof(struct btrfs_header, fsid);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 1c5e097..709e33d 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -159,17 +159,19 @@
 			from_this_trans = 1;
 
 		/* FIXME, this is not good */
-		if (from_this_trans == 0 &&
-		    memcmp_extent_buffer(buf, result, 0, BTRFS_CRC32_SIZE)) {
+		if (memcmp_extent_buffer(buf, result, 0, BTRFS_CRC32_SIZE)) {
 			u32 val;
 			u32 found = 0;
 			memcpy(&found, result, BTRFS_CRC32_SIZE);
 
 			read_extent_buffer(buf, &val, 0, BTRFS_CRC32_SIZE);
+			WARN_ON(1);
 			printk("btrfs: %s checksum verify failed on %llu "
-			       "wanted %X found %X from_this_trans %d\n",
+			       "wanted %X found %X from_this_trans %d "
+			       "level %d\n",
 			       root->fs_info->sb->s_id,
-			       buf->start, val, found, from_this_trans);
+			       buf->start, val, found, from_this_trans,
+			       btrfs_header_level(buf));
 			return 1;
 		}
 	} else {
@@ -220,6 +222,9 @@
 		goto err;
 	}
 	found_level = btrfs_header_level(eb);
+	spin_lock(&root->fs_info->hash_lock);
+	btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
+	spin_unlock(&root->fs_info->hash_lock);
 	csum_tree_block(root, eb, 0);
 err:
 	free_extent_buffer(eb);