Btrfs: Deal with failed writes in mirrored configurations
authorChris Mason <chris.mason@oracle.com>
Tue, 29 Apr 2008 13:38:00 +0000 (09:38 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 25 Sep 2008 15:04:02 +0000 (11:04 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/volumes.c
fs/btrfs/volumes.h

index 1c7d84a..e35e701 100644 (file)
@@ -1385,7 +1385,10 @@ int write_all_supers(struct btrfs_root *root)
        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 @@ int write_all_supers(struct btrfs_root *root)
                } 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 @@ int write_all_supers(struct btrfs_root *root)
                                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;
 }
 
index 95aee5a..f94794a 100644 (file)
@@ -315,8 +315,8 @@ struct btrfs_block_group_cache *btrfs_find_block_group(struct btrfs_root *root,
        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);
 
index 9a72411..57ab755 100644 (file)
@@ -1425,6 +1425,7 @@ static int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw,
        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 @@ again:
                                GFP_NOFS);
                if (!multi)
                        return -ENOMEM;
+
+               atomic_set(&multi->error, 0);
        }
 
        spin_lock(&em_tree->lock);
@@ -1462,8 +1465,10 @@ again:
                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 @@ again:
        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 @@ static int end_bio_multi_stripe(struct bio *bio,
                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)
index 3f9a17f..a9663e9 100644 (file)
@@ -90,7 +90,8 @@ struct btrfs_multi_bio {
        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[];
 };