summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/extent-tree.c
diff options
context:
space:
mode:
authorChris Mason2007-08-07 21:52:19 +0200
committerDavid Woodhouse2007-08-07 21:52:19 +0200
commit9f3a742736cecda5a8778be70faa2f779458839f (patch)
tree500ef3701521e63254dbe82e5c71e7ccff746275 /fs/btrfs/extent-tree.c
parentBtrfs: cleaner make clean (diff)
downloadkernel-qcow2-linux-9f3a742736cecda5a8778be70faa2f779458839f.tar.gz
kernel-qcow2-linux-9f3a742736cecda5a8778be70faa2f779458839f.tar.xz
kernel-qcow2-linux-9f3a742736cecda5a8778be70faa2f779458839f.zip
Btrfs: Do snapshot deletion in smaller chunks.
Before, snapshot deletion was a single atomic unit. This caused considerable lock contention and required an unbounded amount of space. Now, the drop_progress field in the root item is used to indicate how far along snapshot deletion is, and to resume where it left off. Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/extent-tree.c')
-rw-r--r--fs/btrfs/extent-tree.c45
1 files changed, 41 insertions, 4 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 5ace2c33f1aa..9455974dabea 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1561,12 +1561,21 @@ static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root
int i;
int slot;
int ret;
+ struct btrfs_root_item *root_item = &root->root_item;
+
for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
slot = path->slots[i];
if (slot < btrfs_header_nritems(
btrfs_buffer_header(path->nodes[i])) - 1) {
+ struct btrfs_node *node;
+ node = btrfs_buffer_node(path->nodes[i]);
path->slots[i]++;
*level = i;
+ WARN_ON(*level == 0);
+ memcpy(&root_item->drop_progress,
+ &node->ptrs[path->slots[i]].key,
+ sizeof(root_item->drop_progress));
+ root_item->drop_level = i;
return 0;
} else {
ret = btrfs_free_extent(trans, root,
@@ -1587,7 +1596,7 @@ static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root
* decremented.
*/
int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
- *root, struct buffer_head *snap)
+ *root)
{
int ret = 0;
int wret;
@@ -1595,14 +1604,33 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
struct btrfs_path *path;
int i;
int orig_level;
+ int num_walks = 0;
+ struct btrfs_root_item *root_item = &root->root_item;
path = btrfs_alloc_path();
BUG_ON(!path);
- level = btrfs_header_level(btrfs_buffer_header(snap));
+ level = btrfs_header_level(btrfs_buffer_header(root->node));
orig_level = level;
- path->nodes[level] = snap;
- path->slots[level] = 0;
+ if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
+ path->nodes[level] = root->node;
+ path->slots[level] = 0;
+ } else {
+ struct btrfs_key key;
+ struct btrfs_disk_key *found_key;
+ struct btrfs_node *node;
+ btrfs_disk_key_to_cpu(&key, &root_item->drop_progress);
+ wret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0) {
+ ret = wret;
+ goto out;
+ }
+ level = root_item->drop_level;
+ node = btrfs_buffer_node(path->nodes[level]);
+ found_key = &node->ptrs[path->slots[level]].key;
+ WARN_ON(memcmp(found_key, &root_item->drop_progress,
+ sizeof(*found_key)));
+ }
while(1) {
wret = walk_down_tree(trans, root, path, &level);
if (wret > 0)
@@ -1615,12 +1643,21 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root
break;
if (wret < 0)
ret = wret;
+ num_walks++;
+ if (num_walks > 10) {
+ struct btrfs_key key;
+ btrfs_disk_key_to_cpu(&key, &root_item->drop_progress);
+ ret = -EAGAIN;
+ get_bh(root->node);
+ break;
+ }
}
for (i = 0; i <= orig_level; i++) {
if (path->nodes[i]) {
btrfs_block_release(root, path->nodes[i]);
}
}
+out:
btrfs_free_path(path);
return ret;
}