Btrfs: Add support for mirroring across drives

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index a34c289a..4ab98d8 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -230,9 +230,13 @@
 			goto new_group;
 		if (start + num  > total_fs_bytes)
 			goto new_group;
+		if (!block_group_bits(cache, data)) {
+			printk("block group bits don't match %Lu %Lu\n", cache->flags, data);
+		}
 		*start_ret = start;
 		return 0;
-	} out:
+	}
+out:
 	cache = btrfs_lookup_block_group(root->fs_info, search_start);
 	if (!cache) {
 		printk("Unable to find block group for %Lu\n", search_start);
@@ -365,14 +369,17 @@
 		if (cache->key.objectid > total_fs_bytes)
 			break;
 
-		if (full_search)
-			free_check = cache->key.offset;
-		else
-			free_check = div_factor(cache->key.offset, factor);
+		if (block_group_bits(cache, data)) {
+			if (full_search)
+				free_check = cache->key.offset;
+			else
+				free_check = div_factor(cache->key.offset,
+							factor);
 
-		if (used + cache->pinned < free_check) {
-			found_group = cache;
-			goto found;
+			if (used + cache->pinned < free_check) {
+				found_group = cache;
+				goto found;
+			}
 		}
 		cond_resched();
 	}
@@ -1038,6 +1045,19 @@
 	return 0;
 }
 
+static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
+{
+	u64 extra_flags = flags & (BTRFS_BLOCK_GROUP_RAID0 |
+				   BTRFS_BLOCK_GROUP_RAID1);
+	if (extra_flags) {
+		if (flags & BTRFS_BLOCK_GROUP_DATA)
+			fs_info->avail_data_alloc_bits |= extra_flags;
+		if (flags & BTRFS_BLOCK_GROUP_METADATA)
+			fs_info->avail_metadata_alloc_bits |= extra_flags;
+		if (flags & BTRFS_BLOCK_GROUP_SYSTEM)
+			fs_info->avail_system_alloc_bits |= extra_flags;
+	}
+}
 
 static int do_chunk_alloc(struct btrfs_trans_handle *trans,
 			  struct btrfs_root *extent_root, u64 alloc_bytes,
@@ -1060,7 +1080,7 @@
 	if (space_info->full)
 		return 0;
 
-	thresh = div_factor(space_info->total_bytes, 7);
+	thresh = div_factor(space_info->total_bytes, 6);
 	if ((space_info->bytes_used + space_info->bytes_pinned + alloc_bytes) <
 	    thresh)
 		return 0;
@@ -1079,16 +1099,7 @@
 		     start, num_bytes);
 	BUG_ON(ret);
 
-	if (flags & BTRFS_BLOCK_GROUP_RAID0) {
-		if (flags & BTRFS_BLOCK_GROUP_DATA) {
-			extent_root->fs_info->extra_data_alloc_bits =
-				BTRFS_BLOCK_GROUP_RAID0;
-		}
-		if (flags & BTRFS_BLOCK_GROUP_METADATA) {
-			extent_root->fs_info->extra_alloc_bits =
-				BTRFS_BLOCK_GROUP_RAID0;
-		}
-	}
+	set_avail_alloc_bits(extent_root->fs_info, flags);
 	return 0;
 }
 
@@ -1529,6 +1540,7 @@
 
 	if (data & BTRFS_BLOCK_GROUP_METADATA) {
 		last_ptr = &root->fs_info->last_alloc;
+		empty_cluster = 256 * 1024;
 	}
 
 	if ((data & BTRFS_BLOCK_GROUP_DATA) && btrfs_test_opt(root, SSD)) {
@@ -1693,6 +1705,7 @@
 	u64 root_used;
 	u64 search_start = 0;
 	u64 new_hint;
+	u64 alloc_profile;
 	u32 sizes[2];
 	struct btrfs_fs_info *info = root->fs_info;
 	struct btrfs_root *extent_root = info->extent_root;
@@ -1700,31 +1713,32 @@
 	struct btrfs_extent_ref *ref;
 	struct btrfs_path *path;
 	struct btrfs_key keys[2];
-	int extra_chunk_alloc_bits = 0;
 
 	if (data) {
-		data = BTRFS_BLOCK_GROUP_DATA | info->extra_data_alloc_bits;
+		alloc_profile = info->avail_data_alloc_bits &
+			        info->data_alloc_profile;
+		data = BTRFS_BLOCK_GROUP_DATA | alloc_profile;
 	} else if (root == root->fs_info->chunk_root) {
-		data = BTRFS_BLOCK_GROUP_SYSTEM;
+		alloc_profile = info->avail_system_alloc_bits &
+			        info->system_alloc_profile;
+		data = BTRFS_BLOCK_GROUP_SYSTEM | alloc_profile;
 	} else {
-		data = BTRFS_BLOCK_GROUP_METADATA | info->extra_alloc_bits;
+		alloc_profile = info->avail_metadata_alloc_bits &
+			        info->metadata_alloc_profile;
+		data = BTRFS_BLOCK_GROUP_METADATA | alloc_profile;
 	}
-	if (btrfs_super_num_devices(&info->super_copy) > 1 &&
-	    !(data & BTRFS_BLOCK_GROUP_SYSTEM))
-		extra_chunk_alloc_bits = BTRFS_BLOCK_GROUP_RAID0;
 
 	if (root->ref_cows) {
 		if (!(data & BTRFS_BLOCK_GROUP_METADATA)) {
 			ret = do_chunk_alloc(trans, root->fs_info->extent_root,
 					     2 * 1024 * 1024,
 					     BTRFS_BLOCK_GROUP_METADATA |
-					     info->extra_alloc_bits |
-					     extra_chunk_alloc_bits);
+					     (info->metadata_alloc_profile &
+					      info->avail_metadata_alloc_bits));
 			BUG_ON(ret);
 		}
 		ret = do_chunk_alloc(trans, root->fs_info->extent_root,
-				     num_bytes + 2 * 1024 * 1024, data |
-				     extra_chunk_alloc_bits);
+				     num_bytes + 2 * 1024 * 1024, data);
 		BUG_ON(ret);
 	}
 
@@ -2046,12 +2060,12 @@
 		if (!next || !btrfs_buffer_uptodate(next)) {
 			free_extent_buffer(next);
 			reada_walk_down(root, cur, path->slots[*level]);
-			next = read_tree_block(root, bytenr, blocksize);
 
-			/* we used to drop the lock above, keep the
-			 * code to double check so that we won't forget
-			 * when we drop the lock again in the future
-			 */
+			mutex_unlock(&root->fs_info->fs_mutex);
+			next = read_tree_block(root, bytenr, blocksize);
+			mutex_lock(&root->fs_info->fs_mutex);
+
+			/* we've dropped the lock, double check */
 			ret = lookup_extent_ref(trans, root, bytenr,
 						blocksize, &refs);
 			BUG_ON(ret);
@@ -2739,16 +2753,7 @@
 		} else if (cache->flags & BTRFS_BLOCK_GROUP_METADATA) {
 			bit = BLOCK_GROUP_METADATA;
 		}
-		if (cache->flags & BTRFS_BLOCK_GROUP_RAID0) {
-			if (cache->flags & BTRFS_BLOCK_GROUP_DATA) {
-				info->extra_data_alloc_bits =
-					BTRFS_BLOCK_GROUP_RAID0;
-			}
-			if (cache->flags & BTRFS_BLOCK_GROUP_METADATA) {
-				info->extra_alloc_bits =
-					BTRFS_BLOCK_GROUP_RAID0;
-			}
-		}
+		set_avail_alloc_bits(info, cache->flags);
 
 		ret = update_space_info(info, cache->flags, found_key.offset,
 					btrfs_block_group_used(&cache->item),