Btrfs: Fixup reference counting on cows

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 756b1cd..ae7f4c0 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -1,7 +1,7 @@
 
 CC=gcc
 CFLAGS = -g -Wall
-headers = radix-tree.h ctree.h disk-io.h kerncompat.h print-tree.h
+headers = radix-tree.h ctree.h disk-io.h kerncompat.h print-tree.h list.h
 objects = ctree.o disk-io.o radix-tree.o mkfs.o extent-tree.o print-tree.o
 
 # if you don't have sparse installed, use ls instead
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 1118986..7281638 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -50,16 +50,17 @@
 	memcpy(&cow->node, &buf->node, sizeof(buf->node));
 	cow->node.header.blocknr = cow->blocknr;
 	*cow_ret = cow;
+	btrfs_inc_ref(root, buf);
 	if (buf == root->node) {
 		root->node = cow;
 		cow->count++;
+		if (buf != root->commit_root)
+			free_extent(root, buf->blocknr, 1);
 		tree_block_release(root, buf);
 	} else {
 		parent->node.blockptrs[parent_slot] = cow->blocknr;
 		BUG_ON(list_empty(&parent->dirty));
-	}
-	if (0 && root != root->extent_root && !is_leaf(cow->node.header.flags)) {
-		btrfs_inc_ref(root, cow);
+		free_extent(root, buf->blocknr, 1);
 	}
 	tree_block_release(root, buf);
 	return 0;
@@ -1018,7 +1019,6 @@
 	slot = path->slots[0];
 	nritems = l->header.nritems;
 	mid = (nritems + 1)/ 2;
-
 	right_buffer = alloc_free_block(root);
 	BUG_ON(!right_buffer);
 	BUG_ON(mid == nritems);
@@ -1170,7 +1170,6 @@
 
 	node = &parent->node;
 	nritems = node->header.nritems;
-
 	if (slot != nritems -1) {
 		memmove(node->keys + slot, node->keys + slot + 1,
 			sizeof(struct key) * (nritems - slot - 1));
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 9fe8ba6..4a7bc4e 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -51,10 +51,12 @@
  */
 struct ctree_root {
 	struct tree_buffer *node;
+	struct tree_buffer *commit_root;
 	struct ctree_root *extent_root;
 	struct key current_insert;
 	int fp;
 	struct radix_tree_root cache_radix;
+	struct radix_tree_root pinned_radix;
 	struct list_head trans;
 	struct list_head cache;
 	int cache_size;
@@ -151,4 +153,6 @@
 int insert_item(struct ctree_root *root, struct key *key, void *data, int data_size);
 int next_leaf(struct ctree_root *root, struct ctree_path *path);
 int leaf_free_space(struct leaf *leaf);
+int btrfs_drop_snapshot(struct ctree_root *root, struct tree_buffer *snap);
+int btrfs_finish_extent_commit(struct ctree_root *root);
 #endif
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 0e1c31e..2fe31c3 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -153,13 +153,24 @@
 	return ret;
 }
 
-int commit_transaction(struct ctree_root *root)
+int commit_transaction(struct ctree_root *root, struct ctree_super_block *s)
 {
-	int ret;
+	int ret = 0;
+
 	ret = __commit_transaction(root);
 	if (!ret && root != root->extent_root)
 		ret = __commit_transaction(root->extent_root);
 	BUG_ON(ret);
+	if (root->commit_root != root->node) {
+		struct tree_buffer *snap = root->commit_root;
+		root->commit_root = root->node;
+		root->node->count++;
+		ret = btrfs_drop_snapshot(root, snap);
+		BUG_ON(ret);
+		tree_block_release(root, snap);
+	}
+        write_ctree_super(root, s);
+	btrfs_finish_extent_commit(root);
 	return ret;
 }
 
@@ -168,10 +179,13 @@
 {
 	INIT_LIST_HEAD(&root->trans);
 	INIT_LIST_HEAD(&root->cache);
+	root->cache_size = 0;
 	root->fp = fp;
 	root->node = NULL;
-	root->node = read_tree_block(root, info->tree_root);
 	root->extent_root = extent_root;
+	root->commit_root = NULL;
+	root->node = read_tree_block(root, info->tree_root);
+	memset(&root->current_insert, 0, sizeof(root->current_insert));
 	return 0;
 }
 
@@ -188,6 +202,8 @@
 		return NULL;
 	}
 	INIT_RADIX_TREE(&root->cache_radix, GFP_KERNEL);
+	INIT_RADIX_TREE(&root->pinned_radix, GFP_KERNEL);
+	INIT_RADIX_TREE(&extent_root->pinned_radix, GFP_KERNEL);
 	INIT_RADIX_TREE(&extent_root->cache_radix, GFP_KERNEL);
 	ret = pread(fp, super, sizeof(struct ctree_super_block),
 		     CTREE_SUPER_INFO_OFFSET(CTREE_BLOCKSIZE));
@@ -204,6 +220,8 @@
 	BUG_ON(ret < 0);
 	__setup_root(root, extent_root, &super->root_info, fp);
 	__setup_root(extent_root, extent_root, &super->extent_info, fp);
+	root->commit_root = root->node;
+	root->node->count++;
 	return root;
 }
 
@@ -236,9 +254,11 @@
 	}
 	return 0;
 }
-int close_ctree(struct ctree_root *root)
+int close_ctree(struct ctree_root *root, struct ctree_super_block *s)
 {
-	commit_transaction(root);
+	commit_transaction(root, s);
+	__commit_transaction(root->extent_root);
+	write_ctree_super(root, s);
 	drop_cache(root->extent_root);
 	drop_cache(root);
 	BUG_ON(!list_empty(&root->trans));
@@ -249,6 +269,7 @@
 		tree_block_release(root, root->node);
 	if (root->extent_root->node)
 		tree_block_release(root->extent_root, root->extent_root->node);
+	tree_block_release(root, root->commit_root);
 	free(root);
 	printf("on close %d blocks are allocated\n", allocated_blocks);
 	return 0;
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index b5dee2f..1c0af7c 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -18,9 +18,9 @@
 int write_tree_block(struct ctree_root *root, struct tree_buffer *buf);
 int dirty_tree_block(struct ctree_root *root, struct tree_buffer *buf);
 int clean_tree_block(struct ctree_root *root, struct tree_buffer *buf);
-int commit_transaction(struct ctree_root *root);
+int commit_transaction(struct ctree_root *root, struct ctree_super_block *s);
 struct ctree_root *open_ctree(char *filename, struct ctree_super_block *s);
-int close_ctree(struct ctree_root *root);
+int close_ctree(struct ctree_root *root, struct ctree_super_block *s);
 void tree_block_release(struct ctree_root *root, struct tree_buffer *buf);
 int write_ctree_super(struct ctree_root *root, struct ctree_super_block *s);
 int mkfs(int fd);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 25d9cd1..0723b7f 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -13,7 +13,8 @@
  * other allocations are done.  The pending tag is also used in the same
  * manner for deletes.
  */
-#define CTREE_EXTENT_PENDING 0
+#define CTREE_EXTENT_PENDING_ADD 0
+#define CTREE_EXTENT_PENDING_DEL 1
 
 static int inc_block_ref(struct ctree_root *root, u64 blocknr)
 {
@@ -27,20 +28,51 @@
 	key.flags = 0;
 	key.offset = 1;
 	ret = search_slot(root->extent_root, &key, &path, 0, 1);
+	if (ret != 0)
+		BUG();
 	BUG_ON(ret != 0);
 	l = &path.nodes[0]->leaf;
 	item = (struct extent_item *)(l->data +
 				      l->items[path.slots[0]].offset);
 	item->refs++;
+
 	BUG_ON(list_empty(&path.nodes[0]->dirty));
 	release_path(root->extent_root, &path);
 	return 0;
 }
 
+static int lookup_block_ref(struct ctree_root *root, u64 blocknr, int *refs)
+{
+	struct ctree_path path;
+	int ret;
+	struct key key;
+	struct leaf *l;
+	struct extent_item *item;
+	init_path(&path);
+	key.objectid = blocknr;
+	key.flags = 0;
+	key.offset = 1;
+	ret = search_slot(root->extent_root, &key, &path, 0, 0);
+	if (ret != 0)
+		BUG();
+	l = &path.nodes[0]->leaf;
+	item = (struct extent_item *)(l->data +
+				      l->items[path.slots[0]].offset);
+	*refs = item->refs;
+	release_path(root->extent_root, &path);
+	return 0;
+}
+
 int btrfs_inc_ref(struct ctree_root *root, struct tree_buffer *buf)
 {
 	u64 blocknr;
 	int i;
+
+	if (root == root->extent_root)
+		return 0;
+	if (is_leaf(buf->node.header.flags))
+		return 0;
+
 	for (i = 0; i < buf->node.header.nritems; i++) {
 		blocknr = buf->node.blockptrs[i];
 		inc_block_ref(root, blocknr);
@@ -48,6 +80,113 @@
 	return 0;
 }
 
+int btrfs_finish_extent_commit(struct ctree_root *root)
+{
+	struct ctree_root *extent_root = root->extent_root;
+	unsigned long gang[8];
+	int ret;
+	int i;
+
+	while(1) {
+		ret = radix_tree_gang_lookup(&extent_root->pinned_radix,
+						 (void **)gang, 0,
+						 ARRAY_SIZE(gang));
+		if (!ret)
+			break;
+		for (i = 0; i < ret; i++)
+			radix_tree_delete(&extent_root->pinned_radix, gang[i]);
+	}
+	return 0;
+}
+
+/*
+ * remove an extent from the root, returns 0 on success
+ */
+int __free_extent(struct ctree_root *root, u64 blocknr, u64 num_blocks)
+{
+	struct ctree_path path;
+	struct key key;
+	struct ctree_root *extent_root = root->extent_root;
+	int ret;
+	struct item *item;
+	struct extent_item *ei;
+	key.objectid = blocknr;
+	key.flags = 0;
+	key.offset = num_blocks;
+
+	init_path(&path);
+	ret = search_slot(extent_root, &key, &path, -1, 1);
+	if (ret) {
+		printf("failed to find %Lu\n", key.objectid);
+		print_tree(extent_root, extent_root->node);
+		printf("failed to find %Lu\n", key.objectid);
+		BUG();
+	}
+	item = path.nodes[0]->leaf.items + path.slots[0];
+	ei = (struct extent_item *)(path.nodes[0]->leaf.data + item->offset);
+	BUG_ON(ei->refs == 0);
+	ei->refs--;
+	if (ei->refs == 0) {
+		if (root == extent_root) {
+			int err;
+			radix_tree_preload(GFP_KERNEL);
+			err = radix_tree_insert(&extent_root->pinned_radix,
+					  blocknr, (void *)blocknr);
+			BUG_ON(err);
+			radix_tree_preload_end();
+		}
+		ret = del_item(extent_root, &path);
+		if (ret)
+			BUG();
+	}
+	release_path(extent_root, &path);
+	return ret;
+}
+
+/*
+ * insert all of the pending extents reserved during the original
+ * allocation.  (CTREE_EXTENT_PENDING).  Returns zero if it all worked out
+ */
+static int insert_pending_extents(struct ctree_root *extent_root)
+{
+	int ret;
+	struct key key;
+	struct extent_item item;
+	struct tree_buffer *gang[4];
+	int i;
+
+	// FIXME -ENOSPC
+	item.owner = extent_root->node->node.header.parentid;
+	item.refs = 1;
+	while(1) {
+		ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix,
+						 (void **)gang, 0,
+						 ARRAY_SIZE(gang),
+						 CTREE_EXTENT_PENDING_ADD);
+		if (!ret)
+			break;
+		for (i = 0; i < ret; i++) {
+			key.objectid = gang[i]->blocknr;
+			key.flags = 0;
+			key.offset = 1;
+			ret = insert_item(extent_root, &key, &item,
+					  sizeof(item));
+			if (ret) {
+				printf("%Lu already in tree\n", key.objectid);
+				print_tree(extent_root, extent_root->node);
+				BUG();
+				// FIXME undo it and return sane
+				return ret;
+			}
+			radix_tree_tag_clear(&extent_root->cache_radix,
+					     gang[i]->blocknr,
+					     CTREE_EXTENT_PENDING_ADD);
+			tree_block_release(extent_root, gang[i]);
+		}
+	}
+	return 0;
+}
+
 /*
  * find all the blocks marked as pending in the radix tree and remove
  * them from the extent map
@@ -55,78 +194,73 @@
 static int del_pending_extents(struct ctree_root *extent_root)
 {
 	int ret;
-	struct key key;
 	struct tree_buffer *gang[4];
 	int i;
-	struct ctree_path path;
 
 	while(1) {
 		ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix,
 						 (void **)gang, 0,
 						 ARRAY_SIZE(gang),
-						 CTREE_EXTENT_PENDING);
+						 CTREE_EXTENT_PENDING_DEL);
 		if (!ret)
 			break;
 		for (i = 0; i < ret; i++) {
-			key.objectid = gang[i]->blocknr;
-			key.flags = 0;
-			key.offset = 1;
-			init_path(&path);
-			ret = search_slot(extent_root, &key, &path, -1, 1);
-			if (ret) {
-				print_tree(extent_root, extent_root->node);
-				printf("unable to find %Lu\n", key.objectid);
-				BUG();
-				// FIXME undo it and return sane
-				return ret;
-			}
-			ret = del_item(extent_root, &path);
-			if (ret) {
-				BUG();
-				return ret;
-			}
-			release_path(extent_root, &path);
+			ret = __free_extent(extent_root, gang[i]->blocknr, 1);
 			radix_tree_tag_clear(&extent_root->cache_radix,
 						gang[i]->blocknr,
-						CTREE_EXTENT_PENDING);
+						CTREE_EXTENT_PENDING_DEL);
 			tree_block_release(extent_root, gang[i]);
 		}
 	}
 	return 0;
 }
 
+static int run_pending(struct ctree_root *extent_root)
+{
+	while(radix_tree_tagged(&extent_root->cache_radix,
+			        CTREE_EXTENT_PENDING_DEL) ||
+	      radix_tree_tagged(&extent_root->cache_radix,
+				CTREE_EXTENT_PENDING_ADD)) {
+		insert_pending_extents(extent_root);
+		del_pending_extents(extent_root);
+	}
+	return 0;
+}
+
+
 /*
  * remove an extent from the root, returns 0 on success
  */
 int free_extent(struct ctree_root *root, u64 blocknr, u64 num_blocks)
 {
-	struct ctree_path path;
 	struct key key;
 	struct ctree_root *extent_root = root->extent_root;
 	struct tree_buffer *t;
 	int pending_ret;
 	int ret;
+
+	if (root == extent_root) {
+		t = find_tree_block(root, blocknr);
+		if (radix_tree_tag_get(&root->cache_radix, blocknr,
+				      CTREE_EXTENT_PENDING_ADD)) {
+			radix_tree_tag_clear(&root->cache_radix,
+					     blocknr,
+					     CTREE_EXTENT_PENDING_ADD);
+			/* once for us */
+			tree_block_release(root, t);
+			/* once for the pending add */
+			tree_block_release(root, t);
+		} else {
+			radix_tree_tag_set(&root->cache_radix, blocknr,
+				   CTREE_EXTENT_PENDING_DEL);
+		}
+		return 0;
+	}
 	key.objectid = blocknr;
 	key.flags = 0;
 	key.offset = num_blocks;
-	if (root == extent_root) {
-		t = read_tree_block(root, key.objectid);
-		radix_tree_tag_set(&root->cache_radix, key.objectid,
-				   CTREE_EXTENT_PENDING);
-		return 0;
-	}
-	init_path(&path);
-	ret = search_slot(extent_root, &key, &path, -1, 1);
-	if (ret) {
-		print_tree(extent_root, extent_root->node);
-		printf("failed to find %Lu\n", key.objectid);
-		BUG();
-	}
-	ret = del_item(extent_root, &path);
-	if (ret)
-		BUG();
-	release_path(extent_root, &path);
-	pending_ret = del_pending_extents(root->extent_root);
+	ret = __free_extent(root, blocknr, num_blocks);
+	pending_ret = run_pending(root->extent_root);
 	return ret ? ret : pending_ret;
 }
 
@@ -203,7 +337,7 @@
 	 */
 	release_path(root, &path);
 	BUG_ON(ins->objectid < search_start);
-	if (orig_root->extent_root == orig_root) {
+	if (1 || orig_root->extent_root == orig_root) {
 		BUG_ON(num_blocks != 1);
 		if ((root->current_insert.objectid <= ins->objectid &&
 		    root->current_insert.objectid +
@@ -211,8 +345,9 @@
 		   (root->current_insert.objectid > ins->objectid &&
 		    root->current_insert.objectid <= ins->objectid +
 		    ins->offset) ||
+		   radix_tree_lookup(&root->pinned_radix, ins->objectid) ||
 		   radix_tree_tag_get(&root->cache_radix, ins->objectid,
-				      CTREE_EXTENT_PENDING)) {
+				      CTREE_EXTENT_PENDING_ADD)) {
 			search_start = ins->objectid + 1;
 			goto check_failed;
 		}
@@ -226,51 +361,6 @@
 }
 
 /*
- * insert all of the pending extents reserved during the original
- * allocation.  (CTREE_EXTENT_PENDING).  Returns zero if it all worked out
- */
-static int insert_pending_extents(struct ctree_root *extent_root)
-{
-	int ret;
-	struct key key;
-	struct extent_item item;
-	struct tree_buffer *gang[4];
-	int i;
-
-	// FIXME -ENOSPC
-	item.refs = 1;
-	item.owner = extent_root->node->node.header.parentid;
-	while(1) {
-		ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix,
-						 (void **)gang, 0,
-						 ARRAY_SIZE(gang),
-						 CTREE_EXTENT_PENDING);
-		if (!ret)
-			break;
-		for (i = 0; i < ret; i++) {
-			key.objectid = gang[i]->blocknr;
-			key.flags = 0;
-			key.offset = 1;
-			ret = insert_item(extent_root, &key, &item,
-					  sizeof(item));
-			if (ret) {
-				printf("%Lu already in tree\n", key.objectid);
-				print_tree(extent_root, extent_root->node);
-				BUG();
-				// FIXME undo it and return sane
-				return ret;
-			}
-			radix_tree_tag_clear(&extent_root->cache_radix,
-					     gang[i]->blocknr,
-					     CTREE_EXTENT_PENDING);
-			printf("%Lu is not pending\n", gang[i]->blocknr);
-			tree_block_release(extent_root, gang[i]);
-		}
-	}
-	return 0;
-}
-
-/*
  * finds a free extent and does all the dirty work required for allocation
  * returns the key for the extent through ins, and a tree buffer for
  * the first block of the extent through buf.
@@ -296,7 +386,7 @@
 				  sizeof(extent_item));
 		memset(&root->extent_root->current_insert, 0,
 		       sizeof(struct key));
-		pending_ret = insert_pending_extents(root->extent_root);
+		pending_ret = run_pending(root->extent_root);
 		if (ret)
 			return ret;
 		if (pending_ret)
@@ -309,9 +399,8 @@
 	BUG_ON(ins->offset != 1);
 	*buf = find_tree_block(root, ins->objectid);
 	BUG_ON(!*buf);
-	printf("%Lu is pending\n", ins->objectid);
 	radix_tree_tag_set(&root->cache_radix, ins->objectid,
-			   CTREE_EXTENT_PENDING);
+			   CTREE_EXTENT_PENDING_ADD);
 	(*buf)->count++;
 	dirty_tree_block(root, *buf);
 	return 0;
@@ -331,13 +420,41 @@
 	ret = alloc_extent(root, 1, 0, (unsigned long)-1,
 			   root->node->node.header.parentid,
 			   &ins, &buf);
-
 	if (ret) {
 		BUG();
 		return NULL;
 	}
 	if (root != root->extent_root)
 		BUG_ON(radix_tree_tag_get(&root->extent_root->cache_radix,
-					  buf->blocknr, CTREE_EXTENT_PENDING));
+					  buf->blocknr,
+					  CTREE_EXTENT_PENDING_ADD));
 	return buf;
 }
+
+int btrfs_drop_snapshot(struct ctree_root *root, struct tree_buffer *snap)
+{
+	int ret;
+	int level;
+	int refs;
+	u64 blocknr = snap->blocknr;
+
+	level = node_level(snap->node.header.flags);
+	ret = lookup_block_ref(root, snap->blocknr, &refs);
+	BUG_ON(ret);
+	if (refs == 1 && level != 0) {
+		struct node *n = &snap->node;
+		struct tree_buffer *b;
+		int i;
+		for (i = 0; i < n->header.nritems; i++) {
+			b = read_tree_block(root, n->blockptrs[i]);
+			/* FIXME, don't recurse here */
+			ret = btrfs_drop_snapshot(root, b);
+			BUG_ON(ret);
+			tree_block_release(root, b);
+		}
+	}
+	ret = free_extent(root, blocknr, 1);
+	BUG_ON(ret);
+	return 0;
+}
+
diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c
index dda08f3..e32a959 100644
--- a/fs/btrfs/print-tree.c
+++ b/fs/btrfs/print-tree.c
@@ -21,9 +21,11 @@
 			item->key.objectid, item->key.flags, item->key.offset,
 			item->offset, item->size);
 		fflush(stdout);
-		printf("\t\titem data %.*s\n", item->size, l->data+item->offset);
+		printf("\t\titem data %.*s\n", item->size,
+			l->data+item->offset);
 		ei = (struct extent_item *)(l->data + item->offset);
-		printf("\t\textent data %u %Lu\n", ei->refs, ei->owner);
+		printf("\t\textent data refs %u owner %Lu\n", ei->refs,
+			ei->owner);
 		fflush(stdout);
 	}
 }
diff --git a/fs/btrfs/quick-test.c b/fs/btrfs/quick-test.c
index 8255f79..6400c71 100644
--- a/fs/btrfs/quick-test.c
+++ b/fs/btrfs/quick-test.c
@@ -19,7 +19,7 @@
 	int i;
 	int num;
 	int ret;
-	int run_size = 1024;
+	int run_size = 100000;
 	int max_key =  100000000;
 	int tree_size = 0;
 	struct ctree_path path;
@@ -44,9 +44,9 @@
 		if (!ret)
 			tree_size++;
 		free(buf);
+
 	}
-	write_ctree_super(root, &super);
-	close_ctree(root);
+	close_ctree(root, &super);
 
 	root = open_ctree("dbfile", &super);
 	printf("starting search\n");
@@ -65,8 +65,7 @@
 		}
 		release_path(root, &path);
 	}
-	write_ctree_super(root, &super);
-	close_ctree(root);
+	close_ctree(root, &super);
 	root = open_ctree("dbfile", &super);
 	printf("node %p level %d total ptrs %d free spc %lu\n", root->node,
 	        node_level(root->node->node.header.flags),
@@ -90,8 +89,7 @@
 		}
 		release_path(root, &path);
 	}
-	write_ctree_super(root, &super);
-	close_ctree(root);
+	close_ctree(root, &super);
 	root = open_ctree("dbfile", &super);
 	srand(128);
 	for (i = 0; i < run_size; i++) {
@@ -106,8 +104,7 @@
 			tree_size++;
 		free(buf);
 	}
-	write_ctree_super(root, &super);
-	close_ctree(root);
+	close_ctree(root, &super);
 	root = open_ctree("dbfile", &super);
 	srand(128);
 	printf("starting search2\n");
@@ -156,10 +153,17 @@
 		}
 		release_path(root, &path);
 	}
+	/*
+	printf("previous tree:\n");
+	print_tree(root, root->commit_root);
+	printf("map before commit\n");
+	print_tree(root->extent_root, root->extent_root->node);
+	*/
+	commit_transaction(root, &super);
 	printf("tree size is now %d\n", tree_size);
+	printf("root %p commit root %p\n", root->node, root->commit_root);
 	printf("map tree\n");
 	print_tree(root->extent_root, root->extent_root->node);
-	write_ctree_super(root, &super);
-	close_ctree(root);
+	close_ctree(root, &super);
 	return 0;
 }
diff --git a/fs/btrfs/random-test.c b/fs/btrfs/random-test.c
index dcc852a..7b37b6b 100644
--- a/fs/btrfs/random-test.c
+++ b/fs/btrfs/random-test.c
@@ -8,6 +8,7 @@
 #include "print-tree.h"
 
 int keep_running = 1;
+struct ctree_super_block super;
 
 static int setup_key(struct radix_tree_root *root, struct key *key, int exists)
 {
@@ -59,11 +60,6 @@
 	return -1;
 }
 
-static int run_commit(struct ctree_root *root, struct radix_tree_root *radix)
-{
-	return commit_transaction(root);
-}
-
 static int insert_dup(struct ctree_root *root, struct radix_tree_root *radix)
 {
 	struct ctree_path path;
@@ -210,7 +206,7 @@
 			goto out;
 		}
 		if (i % 1000 == 0) {
-			ret = commit_transaction(root);
+			ret = commit_transaction(root, &super);
 			if (ret) {
 				fprintf(stderr, "fill commit failed\n");
 				return ret;
@@ -229,7 +225,7 @@
 static int bulk_op(struct ctree_root *root, struct radix_tree_root *radix)
 {
 	int ret;
-	int nr = rand() % 20000;
+	int nr = rand() % 5000;
 	static int run_nr = 0;
 
 	/* do the bulk op much less frequently */
@@ -247,7 +243,7 @@
 
 int (*ops[])(struct ctree_root *root, struct radix_tree_root *radix) =
 	{ ins_one, insert_dup, del_one, lookup_item,
-	  lookup_enoent, bulk_op, run_commit };
+	  lookup_enoent, bulk_op };
 
 static int fill_radix(struct ctree_root *root, struct radix_tree_root *radix)
 {
@@ -314,7 +310,6 @@
 int main(int ac, char **av)
 {
 	RADIX_TREE(radix, GFP_KERNEL);
-	struct ctree_super_block super;
 	struct ctree_root *root;
 	int i;
 	int ret;
@@ -365,8 +360,7 @@
 			printf("open & close, root level %d nritems %d\n",
 				node_level(root->node->node.header.flags),
 				root->node->node.header.nritems);
-			write_ctree_super(root, &super);
-			close_ctree(root);
+			close_ctree(root, &super);
 			root = open_ctree("dbfile", &super);
 		}
 		while(count--) {
@@ -380,7 +374,7 @@
 				err = ret;
 				goto out;
 			}
-			if (ops[op] == bulk_op || ops[op] == run_commit)
+			if (ops[op] == bulk_op)
 				break;
 			if (keep_running == 0) {
 				err = 0;
@@ -389,8 +383,7 @@
 		}
 	}
 out:
-	write_ctree_super(root, &super);
-	close_ctree(root);
+	close_ctree(root, &super);
 	return err;
 }