diff options
-rw-r--r-- | cmds-check.c | 58 | ||||
-rw-r--r-- | ctree.c | 7 | ||||
-rw-r--r-- | ctree.h | 1 | ||||
-rw-r--r-- | disk-io.c | 15 | ||||
-rw-r--r-- | extent_io.c | 2 | ||||
-rw-r--r-- | extent_io.h | 2 |
6 files changed, 84 insertions, 1 deletions
diff --git a/cmds-check.c b/cmds-check.c index 7c21358d..59ae4ecc 100644 --- a/cmds-check.c +++ b/cmds-check.c @@ -5958,6 +5958,47 @@ static int reinit_extent_tree(struct btrfs_fs_info *fs_info) return btrfs_commit_transaction(trans, fs_info->extent_root); } +static int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb) +{ + struct btrfs_path *path; + struct btrfs_trans_handle *trans; + struct btrfs_key key; + int ret; + + printf("Recowing metadata block %llu\n", eb->start); + key.objectid = btrfs_header_owner(eb); + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + + root = btrfs_read_fs_root(root->fs_info, &key); + if (IS_ERR(root)) { + fprintf(stderr, "Couldn't find owner root %llu\n", + key.objectid); + return PTR_ERR(root); + } + + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; + + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + btrfs_free_path(path); + return PTR_ERR(trans); + } + + path->lowest_level = btrfs_header_level(eb); + if (path->lowest_level) + btrfs_node_key_to_cpu(eb, &key, 0); + else + btrfs_item_key_to_cpu(eb, &key, 0); + + ret = btrfs_search_slot(trans, root, &key, path, 0, 1); + btrfs_commit_transaction(trans, root); + btrfs_free_path(path); + return ret; +} + static struct option long_options[] = { { "super", 1, NULL, 's' }, { "repair", 0, NULL, 0 }, @@ -6108,6 +6149,23 @@ int cmd_check(int argc, char **argv) fprintf(stderr, "checking root refs\n"); ret = check_root_refs(root, &root_cache); + if (ret) + goto out; + + while (repair && !list_empty(&root->fs_info->recow_ebs)) { + struct extent_buffer *eb; + + eb = list_first_entry(&root->fs_info->recow_ebs, + struct extent_buffer, recow); + ret = recow_extent_buffer(root, eb); + if (ret) + break; + } + + if (!list_empty(&root->fs_info->recow_ebs)) { + fprintf(stderr, "Transid errors in file system\n"); + ret = 1; + } out: free_root_recs_tree(&root_cache); close_ctree(root); @@ -293,7 +293,8 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans, write_extent_buffer(cow, root->fs_info->fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE); - WARN_ON(btrfs_header_generation(buf) > trans->transid); + WARN_ON(!(buf->flags & EXTENT_BAD_TRANSID) && + btrfs_header_generation(buf) > trans->transid); update_ref_for_cow(trans, root, buf, cow); @@ -317,6 +318,10 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans, btrfs_free_extent(trans, root, buf->start, buf->len, 0, root->root_key.objectid, level, 1); } + if (!list_empty(&buf->recow)) { + list_del_init(&buf->recow); + free_extent_buffer(buf); + } free_extent_buffer(buf); btrfs_mark_buffer_dirty(cow); *cow_ret = cow; @@ -966,6 +966,7 @@ struct btrfs_fs_info { struct btrfs_extent_ops *extent_ops; struct list_head dirty_cowonly_roots; + struct list_head recow_ebs; struct btrfs_fs_devices *fs_devices; struct list_head space_info; @@ -180,6 +180,7 @@ static int verify_parent_transid(struct extent_io_tree *io_tree, (unsigned long long)parent_transid, (unsigned long long)btrfs_header_generation(eb)); if (ignore) { + eb->flags |= EXTENT_BAD_TRANSID; printk("Ignoring transid failure\n"); return 0; } @@ -274,6 +275,12 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, csum_tree_block(root, eb, 1) == 0 && verify_parent_transid(eb->tree, eb, parent_transid, ignore) == 0) { + if (eb->flags & EXTENT_BAD_TRANSID && + list_empty(&eb->recow)) { + list_add_tail(&eb->recow, + &root->fs_info->recow_ebs); + eb->refs++; + } btrfs_set_buffer_uptodate(eb); return eb; } @@ -749,6 +756,7 @@ struct btrfs_fs_info *btrfs_new_fs_info(int writable, u64 sb_bytenr) mutex_init(&fs_info->fs_mutex); INIT_LIST_HEAD(&fs_info->dirty_cowonly_roots); INIT_LIST_HEAD(&fs_info->space_info); + INIT_LIST_HEAD(&fs_info->recow_ebs); if (!writable) fs_info->readonly = 1; @@ -900,6 +908,13 @@ FREE_EXTENT_CACHE_BASED_TREE(mapping_cache, free_map_lookup); void btrfs_cleanup_all_caches(struct btrfs_fs_info *fs_info) { + while (!list_empty(&fs_info->recow_ebs)) { + struct extent_buffer *eb; + eb = list_first_entry(&fs_info->recow_ebs, + struct extent_buffer, recow); + list_del_init(&eb->recow); + free_extent_buffer(eb); + } free_mapping_cache_tree(&fs_info->mapping_tree.cache_tree); extent_io_tree_cleanup(&fs_info->extent_cache); extent_io_tree_cleanup(&fs_info->free_space_cache); diff --git a/extent_io.c b/extent_io.c index 1c481d5d..ad07b9cd 100644 --- a/extent_io.c +++ b/extent_io.c @@ -587,6 +587,7 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree, eb->dev_bytenr = (u64)-1; eb->cache_node.start = bytenr; eb->cache_node.size = blocksize; + INIT_LIST_HEAD(&eb->recow); free_some_buffers(tree); ret = insert_cache_extent(&tree->cache, &eb->cache_node); @@ -610,6 +611,7 @@ void free_extent_buffer(struct extent_buffer *eb) struct extent_io_tree *tree = eb->tree; BUG_ON(eb->flags & EXTENT_DIRTY); list_del_init(&eb->lru); + list_del_init(&eb->recow); remove_cache_extent(&tree->cache, &eb->cache_node); BUG_ON(tree->cache_size < eb->len); tree->cache_size -= eb->len; diff --git a/extent_io.h b/extent_io.h index ad283cdc..68fb9cef 100644 --- a/extent_io.h +++ b/extent_io.h @@ -39,6 +39,7 @@ #define EXTENT_DEFRAG_DONE (1 << 7) #define EXTENT_BUFFER_FILLED (1 << 8) #define EXTENT_CSUM (1 << 9) +#define EXTENT_BAD_TRANSID (1 << 10) #define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK) #define BLOCK_GROUP_DATA EXTENT_WRITEBACK @@ -72,6 +73,7 @@ struct extent_buffer { u32 len; struct extent_io_tree *tree; struct list_head lru; + struct list_head recow; int refs; int flags; int fd; |