Btrfs: more block allocator work

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 4199172..c432222 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -250,6 +250,8 @@
 struct btrfs_block_group_cache {
 	struct btrfs_key key;
 	struct btrfs_block_group_item item;
+	u64 first_free;
+	u64 last_alloc;
 };
 
 struct crypto_hash;
@@ -257,7 +259,7 @@
 	struct btrfs_root *extent_root;
 	struct btrfs_root *tree_root;
 	struct btrfs_root *dev_root;
-	struct btrfs_key last_insert;
+	struct btrfs_block_group_cache *block_group_cache;
 	struct radix_tree_root fs_roots_radix;
 	struct radix_tree_root pending_del_radix;
 	struct radix_tree_root pinned_radix;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 1c27eb6..2489ffa 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -558,7 +558,7 @@
 	}
 	mutex_init(&fs_info->trans_mutex);
 	mutex_init(&fs_info->fs_mutex);
-	memset(&fs_info->last_insert, 0, sizeof(fs_info->last_insert));
+	fs_info->block_group_cache = NULL;
 
 	__setup_root(sb->s_blocksize, dev_root,
 		     fs_info, BTRFS_DEV_TREE_OBJECTID);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 0bb4fc8..71e3b31 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -12,6 +12,63 @@
 static int del_pending_extents(struct btrfs_trans_handle *trans, struct
 			       btrfs_root *extent_root);
 
+static int find_search_start(struct btrfs_root *root, int data)
+{
+	struct btrfs_block_group_cache *cache[8];
+	struct btrfs_fs_info *info = root->fs_info;
+	u64 used;
+	u64 last;
+	int i;
+	int ret;
+
+	cache[0] = info->block_group_cache;
+	if (!cache[0])
+		goto find_new;
+	used = btrfs_block_group_used(&cache[0]->item);
+	if (used < (cache[0]->key.offset * 3 / 2))
+		return 0;
+find_new:
+	last = 0;
+	while(1) {
+		ret = radix_tree_gang_lookup_tag(&info->block_group_radix,
+						 (void **)cache,
+						 last, ARRAY_SIZE(cache),
+						 BTRFS_BLOCK_GROUP_DIRTY);
+		if (!ret)
+			break;
+		for (i = 0; i < ret; i++) {
+			used = btrfs_block_group_used(&cache[i]->item);
+			if (used < (cache[i]->key.offset * 3 / 2)) {
+				info->block_group_cache = cache[i];
+				cache[i]->last_alloc = cache[i]->first_free;
+				return 0;
+			}
+			last = cache[i]->key.objectid +
+				cache[i]->key.offset - 1;
+		}
+	}
+	last = 0;
+	while(1) {
+		ret = radix_tree_gang_lookup(&info->block_group_radix,
+						 (void **)cache,
+						 last, ARRAY_SIZE(cache));
+		if (!ret)
+			break;
+		for (i = 0; i < ret; i++) {
+			used = btrfs_block_group_used(&cache[i]->item);
+			if (used < (cache[i]->key.offset * 3 / 2)) {
+				info->block_group_cache = cache[i];
+				cache[i]->last_alloc = cache[i]->first_free;
+				return 0;
+			}
+			last = cache[i]->key.objectid +
+				cache[i]->key.offset - 1;
+		}
+	}
+	info->block_group_cache = NULL;
+	return 0;
+}
+
 int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
 				struct btrfs_root *root,
 				u64 blocknr, u64 num_blocks)
@@ -205,8 +262,11 @@
 	while(total) {
 		ret = radix_tree_gang_lookup(&info->block_group_radix,
 					     (void **)&cache, blocknr, 1);
-		if (!ret)
+		if (!ret) {
+			printk(KERN_CRIT "blocknr %Lu lookup failed\n",
+			       blocknr);
 			return -1;
+		}
 		block_in_group = blocknr - cache->key.objectid;
 		WARN_ON(block_in_group > cache->key.offset);
 		radix_tree_tag_set(&info->block_group_radix,
@@ -217,10 +277,15 @@
 		num = min(total, cache->key.offset - block_in_group);
 		total -= num;
 		blocknr += num;
-		if (alloc)
+		if (alloc) {
 			old_val += num;
-		else
+			if (blocknr > cache->last_alloc)
+				cache->last_alloc = blocknr;
+		} else {
 			old_val -= num;
+			if (blocknr < cache->first_free)
+				cache->first_free = blocknr;
+		}
 		btrfs_set_block_group_used(&cache->item, old_val);
 	}
 	return 0;
@@ -246,9 +311,7 @@
 			clear_radix_bit(pinned_radix, gang[i]);
 		}
 	}
-	if (root->fs_info->last_insert.objectid > first)
-		root->fs_info->last_insert.objectid = first;
-	root->fs_info->last_insert.offset = 0;
+	root->fs_info->block_group_cache = NULL;
 	return 0;
 }
 
@@ -466,8 +529,10 @@
 		num_blocks = 1;
 		total_needed = min(level + 2, BTRFS_MAX_LEVEL) * 3;
 	}
-	if (info->last_insert.objectid > search_start)
-		search_start = info->last_insert.objectid;
+	find_search_start(root, 0);
+	if (info->block_group_cache &&
+	    info->block_group_cache->last_alloc > search_start)
+		search_start = info->block_group_cache->last_alloc;
 
 check_failed:
 	btrfs_init_path(path);
@@ -567,8 +632,7 @@
 		      total_found < total_needed) {
 			nr = total_needed - total_found - 1;
 			BUG_ON(nr < 0);
-			root->fs_info->extent_tree_prealloc[nr] =
-				test_block;
+			info->extent_tree_prealloc[nr] = test_block;
 			total_found++;
 			test_block++;
 		}
@@ -576,9 +640,14 @@
 			search_start = test_block;
 			goto check_failed;
 		}
-		root->fs_info->extent_tree_prealloc_nr = total_found;
+		info->extent_tree_prealloc_nr = total_found;
 	}
-	root->fs_info->last_insert.objectid = ins->objectid;
+	ret = radix_tree_gang_lookup(&info->block_group_radix,
+				     (void **)&info->block_group_cache,
+				     ins->objectid, 1);
+	if (ret) {
+		info->block_group_cache->last_alloc = ins->objectid;
+	}
 	ins->offset = num_blocks;
 	btrfs_free_path(path);
 	return 0;
@@ -915,6 +984,8 @@
 				    struct btrfs_block_group_item);
 		memcpy(&cache->item, bi, sizeof(*bi));
 		memcpy(&cache->key, &found_key, sizeof(found_key));
+		cache->last_alloc = 0;
+		cache->first_free = 0;
 		key.objectid = found_key.objectid + found_key.offset;
 		btrfs_release_path(root, path);
 		ret = radix_tree_insert(&root->fs_info->block_group_radix,
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index edcebf7..a10e902 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -223,6 +223,7 @@
 		btrfs_update_inode(trans, root, dir);
 		drop_nlink(dentry->d_inode);
 		btrfs_update_inode(trans, root, dentry->d_inode);
+		dir->i_sb->s_dirt = 1;
 	}
 	return ret;
 }
@@ -411,6 +412,7 @@
 error:
 	btrfs_release_path(root, path);
 	btrfs_free_path(path);
+	inode->i_sb->s_dirt = 1;
 	return ret;
 }
 
@@ -935,6 +937,7 @@
 		goto out_fail;
 	d_instantiate(dentry, inode);
 	drop_on_err = 0;
+	dir->i_sb->s_dirt = 1;
 
 out_fail:
 	btrfs_end_transaction(trans, root);