Btrfs: Deal with failed writes in mirrored configurations

Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 1c7d84a..e35e701 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1385,7 +1385,10 @@
 	struct buffer_head *bh;
 	int ret;
 	int do_barriers;
+	int max_errors;
+	int total_errors = 0;
 
+	max_errors = btrfs_super_num_devices(&root->fs_info->super_copy) - 1;
 	do_barriers = !btrfs_test_opt(root, NOBARRIER);
 
 	sb = root->fs_info->sb_buffer;
@@ -1433,8 +1436,14 @@
 		} else {
 			ret = submit_bh(WRITE, bh);
 		}
-		BUG_ON(ret);
+		if (ret)
+			total_errors++;
 	}
+	if (total_errors > max_errors) {
+		printk("btrfs: %d errors while writing supers\n", total_errors);
+		BUG();
+	}
+	total_errors = 0;
 
 	list_for_each(cur, head) {
 		dev = list_entry(cur, struct btrfs_device, dev_list);
@@ -1454,13 +1463,17 @@
 				wait_on_buffer(bh);
 				BUG_ON(!buffer_uptodate(bh));
 			} else {
-				BUG();
+				total_errors++;
 			}
 
 		}
 		dev->pending_io = NULL;
 		brelse(bh);
 	}
+	if (total_errors > max_errors) {
+		printk("btrfs: %d errors while writing supers\n", total_errors);
+		BUG();
+	}
 	return 0;
 }
 
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 95aee5a..f94794a 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -315,8 +315,8 @@
 	block_group_cache = &info->block_group_cache;
 	total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
 
-	if (!owner)
-		factor = 10;
+	if (data & BTRFS_BLOCK_GROUP_METADATA)
+		factor = 9;
 
 	bit = block_group_state_bits(data);
 
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 9a72411..57ab755 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -1425,6 +1425,7 @@
 	int stripe_index;
 	int i;
 	int num_stripes;
+	int max_errors = 0;
 	struct btrfs_multi_bio *multi = NULL;
 
 	if (multi_ret && !(rw & (1 << BIO_RW))) {
@@ -1436,6 +1437,8 @@
 				GFP_NOFS);
 		if (!multi)
 			return -ENOMEM;
+
+		atomic_set(&multi->error, 0);
 	}
 
 	spin_lock(&em_tree->lock);
@@ -1462,8 +1465,10 @@
 		if (map->type & (BTRFS_BLOCK_GROUP_RAID1 |
 				 BTRFS_BLOCK_GROUP_DUP)) {
 			stripes_required = map->num_stripes;
+			max_errors = 1;
 		} else if (map->type & BTRFS_BLOCK_GROUP_RAID10) {
 			stripes_required = map->sub_stripes;
+			max_errors = 1;
 		}
 	}
 	if (multi_ret && rw == WRITE &&
@@ -1561,6 +1566,7 @@
 	if (multi_ret) {
 		*multi_ret = multi;
 		multi->num_stripes = num_stripes;
+		multi->max_errors = max_errors;
 	}
 out:
 	free_extent_map(em);
@@ -1598,14 +1604,19 @@
 		return 1;
 #endif
 	if (err)
-		multi->error = err;
+		atomic_inc(&multi->error);
 
 	if (atomic_dec_and_test(&multi->stripes_pending)) {
 		bio->bi_private = multi->private;
 		bio->bi_end_io = multi->end_io;
 
-		if (!err && multi->error)
-			err = multi->error;
+		/* only send an error to the higher layers if it is
+		 * beyond the tolerance of the multi-bio
+		 */
+		if (atomic_read(&multi->error) > multi->max_errors)
+			err = -EIO;
+		else
+			err = 0;
 		kfree(multi);
 
 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 3f9a17f..a9663e92 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -90,7 +90,8 @@
 	atomic_t stripes_pending;
 	bio_end_io_t *end_io;
 	void *private;
-	int error;
+	atomic_t error;
+	int max_errors;
 	int num_stripes;
 	struct btrfs_bio_stripe stripes[];
 };