btrfsprogs/0062-Btrfs-progs-keep-track-of-transid-failures-and-fix-t.patch
Tomáš Chvátal 5be20c4ea7 Accepting request 205320 from home:dsterba:branches:filesystems
SR: a few fixes, aimed for 13.1 RC2

- fsck updates
- more mkfs sanity checks
- qgroup rescan wait

OBS-URL: https://build.opensuse.org/request/show/205320
OBS-URL: https://build.opensuse.org/package/show/filesystems/btrfsprogs?expand=0&rev=144
2013-10-31 09:12:04 +00:00

229 lines
7.1 KiB
Diff

From 174273941f266c5ba71da02cc4d71a95ca41bc20 Mon Sep 17 00:00:00 2001
From: Josef Bacik <jbacik@fusionio.com>
Date: Tue, 1 Oct 2013 09:00:19 -0400
Subject: [PATCH 62/62] Btrfs-progs: keep track of transid failures and fix
them if possible
A user was reporting an issue with bad transid errors on his blocks. The thing
is that btrfs-progs will ignore transid failures for things like restore and
fsck so we can do a best effort to fix a users file system. So fsck can put
together a coherent view of the file system with stale blocks. So if everything
else is ok in the mind of fsck then we can recow these blocks to fix the
generation and the user can get their file system back. Thanks,
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
---
cmds-check.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ctree.c | 7 ++++++-
ctree.h | 1 +
disk-io.c | 15 +++++++++++++++
extent_io.c | 2 ++
extent_io.h | 2 ++
6 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/cmds-check.c b/cmds-check.c
index dbb41e5a4d5b..924aac08f350 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -5675,6 +5675,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 },
@@ -5826,6 +5867,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);
diff --git a/ctree.c b/ctree.c
index 1a4f3f06f38a..e7ccfa03fb0e 100644
--- a/ctree.c
+++ b/ctree.c
@@ -346,7 +346,8 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans,
(unsigned long)btrfs_header_fsid(cow),
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);
@@ -370,6 +371,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;
diff --git a/ctree.h b/ctree.h
index 90be7abe9ebf..61cd93b91937 100644
--- a/ctree.h
+++ b/ctree.h
@@ -952,6 +952,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;
diff --git a/disk-io.c b/disk-io.c
index 1b91de6fc90d..d97ff8c706c9 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -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;
}
@@ -748,6 +755,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;
@@ -899,6 +907,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 464bd07e8d1a..398ee26b4a79 100644
--- a/extent_io.c
+++ b/extent_io.c
@@ -585,6 +585,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);
@@ -608,6 +609,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 2604dcef31e3..45080c2661ae 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;
--
1.8.3.1