Btrfs: deal with free space cache errors while replaying log
authorJosef Bacik <jbacik@fusionio.com>
Thu, 25 Apr 2013 19:55:30 +0000 (15:55 -0400)
committerJosef Bacik <jbacik@fusionio.com>
Mon, 6 May 2013 19:55:20 +0000 (15:55 -0400)
So everybody who got hit by my fsync bug will still continue to hit this
BUG_ON() in the free space cache, which is pretty heavy handed.  So I took a
file system that had this bug and fixed up all the BUG_ON()'s and leaks that
popped up when I tried to mount a broken file system like this.  With this patch
we just fail to mount instead of panicing.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fusionio.com>
fs/btrfs/extent-tree.c
fs/btrfs/free-space-cache.c
fs/btrfs/tree-log.c

index ef4ce2c026d6a69a8d98bfa7ea3eae7fa9615382..b0a3fab98713c41f635af998f4c402fef85d5e8c 100644 (file)
@@ -5210,9 +5210,11 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_root *root,
                                    u64 bytenr, u64 num_bytes)
 {
        struct btrfs_block_group_cache *cache;
+       int ret;
 
        cache = btrfs_lookup_block_group(root->fs_info, bytenr);
-       BUG_ON(!cache); /* Logic error */
+       if (!cache)
+               return -EINVAL;
 
        /*
         * pull in the free space cache (if any) so that our pin
@@ -5225,9 +5227,9 @@ int btrfs_pin_extent_for_log_replay(struct btrfs_root *root,
        pin_down_extent(root, cache, bytenr, num_bytes, 0);
 
        /* remove us from the free space cache (if we're there at all) */
-       btrfs_remove_free_space(cache, bytenr, num_bytes);
+       ret = btrfs_remove_free_space(cache, bytenr, num_bytes);
        btrfs_put_block_group(cache);
-       return 0;
+       return ret;
 }
 
 /**
@@ -6611,40 +6613,42 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
        if (!caching_ctl) {
                BUG_ON(!block_group_cache_done(block_group));
                ret = btrfs_remove_free_space(block_group, start, num_bytes);
-               BUG_ON(ret); /* -ENOMEM */
+               if (ret)
+                       goto out;
        } else {
                mutex_lock(&caching_ctl->mutex);
 
                if (start >= caching_ctl->progress) {
                        ret = add_excluded_extent(root, start, num_bytes);
-                       BUG_ON(ret); /* -ENOMEM */
                } else if (start + num_bytes <= caching_ctl->progress) {
                        ret = btrfs_remove_free_space(block_group,
                                                      start, num_bytes);
-                       BUG_ON(ret); /* -ENOMEM */
                } else {
                        num_bytes = caching_ctl->progress - start;
                        ret = btrfs_remove_free_space(block_group,
                                                      start, num_bytes);
-                       BUG_ON(ret); /* -ENOMEM */
+                       if (ret)
+                               goto out_lock;
 
                        start = caching_ctl->progress;
                        num_bytes = ins->objectid + ins->offset -
                                    caching_ctl->progress;
                        ret = add_excluded_extent(root, start, num_bytes);
-                       BUG_ON(ret); /* -ENOMEM */
                }
-
+out_lock:
                mutex_unlock(&caching_ctl->mutex);
                put_caching_control(caching_ctl);
+               if (ret)
+                       goto out;
        }
 
        ret = btrfs_update_reserved_bytes(block_group, ins->offset,
                                          RESERVE_ALLOC_NO_ACCOUNT);
        BUG_ON(ret); /* logic error */
-       btrfs_put_block_group(block_group);
        ret = alloc_reserved_file_extent(trans, root, 0, root_objectid,
                                         0, owner, offset, ins, 1);
+out:
+       btrfs_put_block_group(block_group);
        return ret;
 }
 
index fa1a8140bfb5550229b44406943d66f2ad0aa59f..37b2b89a28f6cbf630e1fccd38a89079869c98ad 100644 (file)
@@ -1567,7 +1567,8 @@ again:
        search_bytes = ctl->unit;
        search_bytes = min(search_bytes, end - search_start + 1);
        ret = search_bitmap(ctl, bitmap_info, &search_start, &search_bytes);
-       BUG_ON(ret < 0 || search_start != *offset);
+       if (ret < 0 || search_start != *offset)
+               return -EINVAL;
 
        /* We may have found more bits than what we need */
        search_bytes = min(search_bytes, *bytes);
@@ -1973,7 +1974,6 @@ again:
                re_search = true;
                goto again;
        }
-       BUG_ON(ret); /* logic error */
 out_lock:
        spin_unlock(&ctl->tree_lock);
 out:
index f50137a98fb1ac524f9c4bbdbd16011e99515f5e..aebfb2d7b7d2854eb4d0902d7d4116d34bbf2e86 100644 (file)
@@ -277,17 +277,19 @@ static int process_one_buffer(struct btrfs_root *log,
                              struct extent_buffer *eb,
                              struct walk_control *wc, u64 gen)
 {
+       int ret = 0;
+
        if (wc->pin)
-               btrfs_pin_extent_for_log_replay(log->fs_info->extent_root,
-                                               eb->start, eb->len);
+               ret = btrfs_pin_extent_for_log_replay(log->fs_info->extent_root,
+                                                     eb->start, eb->len);
 
-       if (btrfs_buffer_uptodate(eb, gen, 0)) {
+       if (!ret && btrfs_buffer_uptodate(eb, gen, 0)) {
                if (wc->write)
                        btrfs_write_tree_block(eb);
                if (wc->wait)
                        btrfs_wait_tree_block_writeback(eb);
        }
-       return 0;
+       return ret;
 }
 
 /*
@@ -623,7 +625,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
                                                ins.objectid, ins.offset,
                                                0, root->root_key.objectid,
                                                key->objectid, offset, 0);
-                               BUG_ON(ret);
+                               if (ret)
+                                       goto out;
                        } else {
                                /*
                                 * insert the extent pointer in the extent
@@ -632,7 +635,8 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
                                ret = btrfs_alloc_logged_file_extent(trans,
                                                root, root->root_key.objectid,
                                                key->objectid, offset, &ins);
-                               BUG_ON(ret);
+                               if (ret)
+                                       goto out;
                        }
                        btrfs_release_path(path);
 
@@ -1952,11 +1956,13 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
                        if (S_ISDIR(mode)) {
                                ret = replay_dir_deletes(wc->trans,
                                         root, log, path, key.objectid, 0);
-                               BUG_ON(ret);
+                               if (ret)
+                                       break;
                        }
                        ret = overwrite_item(wc->trans, root, path,
                                             eb, i, &key);
-                       BUG_ON(ret);
+                       if (ret)
+                               break;
 
                        /* for regular files, make sure corresponding
                         * orhpan item exist. extents past the new EOF
@@ -1965,12 +1971,14 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
                        if (S_ISREG(mode)) {
                                ret = insert_orphan_item(wc->trans, root,
                                                         key.objectid);
-                               BUG_ON(ret);
+                               if (ret)
+                                       break;
                        }
 
                        ret = link_to_fixup_dir(wc->trans, root,
                                                path, key.objectid);
-                       BUG_ON(ret);
+                       if (ret)
+                               break;
                }
                if (wc->stage < LOG_WALK_REPLAY_ALL)
                        continue;
@@ -1979,28 +1987,35 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
                if (key.type == BTRFS_XATTR_ITEM_KEY) {
                        ret = overwrite_item(wc->trans, root, path,
                                             eb, i, &key);
-                       BUG_ON(ret);
+                       if (ret)
+                               break;
                } else if (key.type == BTRFS_INODE_REF_KEY) {
                        ret = add_inode_ref(wc->trans, root, log, path,
                                            eb, i, &key);
-                       BUG_ON(ret && ret != -ENOENT);
+                       if (ret && ret != -ENOENT)
+                               break;
+                       ret = 0;
                } else if (key.type == BTRFS_INODE_EXTREF_KEY) {
                        ret = add_inode_ref(wc->trans, root, log, path,
                                            eb, i, &key);
-                       BUG_ON(ret && ret != -ENOENT);
+                       if (ret && ret != -ENOENT)
+                               break;
+                       ret = 0;
                } else if (key.type == BTRFS_EXTENT_DATA_KEY) {
                        ret = replay_one_extent(wc->trans, root, path,
                                                eb, i, &key);
-                       BUG_ON(ret);
+                       if (ret)
+                               break;
                } else if (key.type == BTRFS_DIR_ITEM_KEY ||
                           key.type == BTRFS_DIR_INDEX_KEY) {
                        ret = replay_one_dir_item(wc->trans, root, path,
                                                  eb, i, &key);
-                       BUG_ON(ret);
+                       if (ret)
+                               break;
                }
        }
        btrfs_free_path(path);
-       return 0;
+       return ret;
 }
 
 static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
@@ -2045,8 +2060,10 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
 
                if (*level == 1) {
                        ret = wc->process_func(root, next, wc, ptr_gen);
-                       if (ret)
+                       if (ret) {
+                               free_extent_buffer(next);
                                return ret;
+                       }
 
                        path->slots[*level]++;
                        if (wc->free) {
@@ -3970,6 +3987,9 @@ again:
                wc.replay_dest = btrfs_read_fs_root_no_name(fs_info, &tmp_key);
                if (IS_ERR(wc.replay_dest)) {
                        ret = PTR_ERR(wc.replay_dest);
+                       free_extent_buffer(log->node);
+                       free_extent_buffer(log->commit_root);
+                       kfree(log);
                        btrfs_error(fs_info, ret, "Couldn't read target root "
                                    "for tree log recovery.");
                        goto error;
@@ -3978,12 +3998,10 @@ again:
                wc.replay_dest->log_root = log;
                btrfs_record_root_in_trans(trans, wc.replay_dest);
                ret = walk_log_tree(trans, log, &wc);
-               BUG_ON(ret);
 
-               if (wc.stage == LOG_WALK_REPLAY_ALL) {
+               if (!ret && wc.stage == LOG_WALK_REPLAY_ALL) {
                        ret = fixup_inode_link_counts(trans, wc.replay_dest,
                                                      path);
-                       BUG_ON(ret);
                }
 
                key.offset = found_key.offset - 1;
@@ -3992,6 +4010,9 @@ again:
                free_extent_buffer(log->commit_root);
                kfree(log);
 
+               if (ret)
+                       goto error;
+
                if (found_key.offset == 0)
                        break;
        }
@@ -4024,6 +4045,8 @@ again:
 
        return 0;
 error:
+       if (wc.trans)
+               btrfs_end_transaction(wc.trans, fs_info->tree_root);
        btrfs_free_path(path);
        return ret;
 }