summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2008-05-08 13:26:18 -0400
committerChris Mason <chris.mason@oracle.com>2008-09-25 11:04:02 -0400
commitbf4ef67924d87b0addb32f084e83a9283496350e (patch)
tree2935d0395028d878e57df73e5fbd2182abc16c41
parenta061fc8da7b990faa41ca503e66faef3ecdeead0 (diff)
downloadlinux-bf4ef67924d87b0addb32f084e83a9283496350e.tar.bz2
Btrfs: Properly find the root for snapshotted blocks during chunk relocation
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r--fs/btrfs/ctree.h2
-rw-r--r--fs/btrfs/extent-tree.c120
-rw-r--r--fs/btrfs/root-tree.c46
3 files changed, 156 insertions, 12 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 33ab165591c5..f4b4677bec77 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1459,6 +1459,8 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
*item);
int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
btrfs_root_item *item, struct btrfs_key *key);
+int btrfs_search_root(struct btrfs_root *root, u64 search_start,
+ u64 *found_objectid);
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid,
struct btrfs_root *latest_root);
/* dir-item.c */
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index c0e67bde8428..8e69b5acfeff 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2422,6 +2422,85 @@ truncate_racing:
}
/*
+ * The back references tell us which tree holds a ref on a block,
+ * but it is possible for the tree root field in the reference to
+ * reflect the original root before a snapshot was made. In this
+ * case we should search through all the children of a given root
+ * to find potential holders of references on a block.
+ *
+ * Instead, we do something a little less fancy and just search
+ * all the roots for a given key/block combination.
+ */
+static int find_root_for_ref(struct btrfs_root *root,
+ struct btrfs_path *path,
+ struct btrfs_key *key0,
+ int level,
+ int file_key,
+ struct btrfs_root **found_root,
+ u64 bytenr)
+{
+ struct btrfs_key root_location;
+ struct btrfs_root *cur_root = *found_root;
+ struct btrfs_file_extent_item *file_extent;
+ u64 root_search_start = BTRFS_FS_TREE_OBJECTID;
+ u64 found_bytenr;
+ int ret;
+ int i;
+
+ root_location.offset = (u64)-1;
+ root_location.type = BTRFS_ROOT_ITEM_KEY;
+ path->lowest_level = level;
+ path->reada = 0;
+ while(1) {
+ ret = btrfs_search_slot(NULL, cur_root, key0, path, 0, 0);
+ found_bytenr = 0;
+ if (ret == 0 && file_key) {
+ struct extent_buffer *leaf = path->nodes[0];
+ file_extent = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_file_extent_item);
+ if (btrfs_file_extent_type(leaf, file_extent) ==
+ BTRFS_FILE_EXTENT_REG) {
+ found_bytenr =
+ btrfs_file_extent_disk_bytenr(leaf,
+ file_extent);
+ }
+ } else if (ret == 0) {
+ if (path->nodes[level])
+ found_bytenr = path->nodes[level]->start;
+ }
+
+ for (i = level; i < BTRFS_MAX_LEVEL; i++) {
+ if (!path->nodes[i])
+ break;
+ free_extent_buffer(path->nodes[i]);
+ path->nodes[i] = NULL;
+ }
+ btrfs_release_path(cur_root, path);
+
+ if (found_bytenr == bytenr) {
+ *found_root = cur_root;
+ ret = 0;
+ goto out;
+ }
+ ret = btrfs_search_root(root->fs_info->tree_root,
+ root_search_start, &root_search_start);
+ if (ret)
+ break;
+
+ root_location.objectid = root_search_start;
+ cur_root = btrfs_read_fs_root_no_name(root->fs_info,
+ &root_location);
+ if (!cur_root) {
+ ret = 1;
+ break;
+ }
+ }
+out:
+ path->lowest_level = 0;
+ return ret;
+}
+
+/*
* note, this releases the path
*/
static int noinline relocate_one_reference(struct btrfs_root *extent_root,
@@ -2430,13 +2509,15 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root,
{
struct inode *inode;
struct btrfs_root *found_root;
- struct btrfs_key *root_location;
+ struct btrfs_key root_location;
+ struct btrfs_key found_key;
struct btrfs_extent_ref *ref;
u64 ref_root;
u64 ref_gen;
u64 ref_objectid;
u64 ref_offset;
int ret;
+ int level;
ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_extent_ref);
@@ -2446,20 +2527,30 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root,
ref_offset = btrfs_ref_offset(path->nodes[0], ref);
btrfs_release_path(extent_root, path);
- root_location = kmalloc(sizeof(*root_location), GFP_NOFS);
- root_location->objectid = ref_root;
+ root_location.objectid = ref_root;
if (ref_gen == 0)
- root_location->offset = 0;
+ root_location.offset = 0;
else
- root_location->offset = (u64)-1;
- root_location->type = BTRFS_ROOT_ITEM_KEY;
+ root_location.offset = (u64)-1;
+ root_location.type = BTRFS_ROOT_ITEM_KEY;
found_root = btrfs_read_fs_root_no_name(extent_root->fs_info,
- root_location);
+ &root_location);
BUG_ON(!found_root);
- kfree(root_location);
if (ref_objectid >= BTRFS_FIRST_FREE_OBJECTID) {
+ found_key.objectid = ref_objectid;
+ found_key.type = BTRFS_EXTENT_DATA_KEY;
+ found_key.offset = ref_offset;
+ level = 0;
+
+ ret = find_root_for_ref(extent_root, path, &found_key,
+ level, 1, &found_root,
+ extent_key->objectid);
+
+ if (ret)
+ goto out;
+
mutex_unlock(&extent_root->fs_info->fs_mutex);
inode = btrfs_iget_locked(extent_root->fs_info->sb,
ref_objectid, found_root);
@@ -2485,12 +2576,9 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root,
mutex_lock(&extent_root->fs_info->fs_mutex);
} else {
struct btrfs_trans_handle *trans;
- struct btrfs_key found_key;
struct extent_buffer *eb;
- int level;
int i;
- trans = btrfs_start_transaction(found_root, 1);
eb = read_tree_block(found_root, extent_key->objectid,
extent_key->offset);
level = btrfs_header_level(eb);
@@ -2502,6 +2590,15 @@ static int noinline relocate_one_reference(struct btrfs_root *extent_root,
free_extent_buffer(eb);
+ ret = find_root_for_ref(extent_root, path, &found_key,
+ level, 0, &found_root,
+ extent_key->objectid);
+
+ if (ret)
+ goto out;
+
+ trans = btrfs_start_transaction(found_root, 1);
+
path->lowest_level = level;
path->reada = 2;
ret = btrfs_search_slot(trans, found_root, &found_key, path,
@@ -2578,6 +2675,7 @@ static int noinline relocate_one_extent(struct btrfs_root *extent_root,
}
if (ret < 0)
goto out;
+ leaf = path->nodes[0];
}
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index 08f19ec88092..8bf21ba0a43b 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -21,6 +21,51 @@
#include "disk-io.h"
#include "print-tree.h"
+/*
+ * returns 0 on finding something, 1 if no more roots are there
+ * and < 0 on error
+ */
+int btrfs_search_root(struct btrfs_root *root, u64 search_start,
+ u64 *found_objectid)
+{
+ struct btrfs_path *path;
+ struct btrfs_key search_key;
+ int ret;
+
+ root = root->fs_info->tree_root;
+ search_key.objectid = search_start;
+ search_key.type = (u8)-1;
+ search_key.offset = (u64)-1;
+
+ path = btrfs_alloc_path();
+ BUG_ON(!path);
+again:
+ ret = btrfs_search_slot(NULL, root, &search_key, path, 0, 0);
+ if (ret < 0)
+ goto out;
+ if (ret == 0) {
+ ret = 1;
+ goto out;
+ }
+ if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret)
+ goto out;
+ }
+ btrfs_item_key_to_cpu(path->nodes[0], &search_key, path->slots[0]);
+ if (search_key.type != BTRFS_ROOT_ITEM_KEY) {
+ search_key.offset++;
+ btrfs_release_path(root, path);
+ goto again;
+ }
+ ret = 0;
+ *found_objectid = search_key.objectid;
+
+out:
+ btrfs_free_path(path);
+ return ret;
+}
+
int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
struct btrfs_root_item *item, struct btrfs_key *key)
{
@@ -55,7 +100,6 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
memcpy(key, &found_key, sizeof(found_key));
ret = 0;
out:
- btrfs_release_path(root, path);
btrfs_free_path(path);
return ret;
}