diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-08-07 15:52:19 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-08-07 15:52:19 -0400 |
commit | 9f3a742736cecda5a8778be70faa2f779458839f (patch) | |
tree | 500ef3701521e63254dbe82e5c71e7ccff746275 /fs/btrfs/extent-tree.c | |
parent | 8578f0f1fda41f8429a1037022b720275102ca65 (diff) |
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.c | 45 |
1 files changed, 41 insertions, 4 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 5ace2c33f1a..9455974dabe 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 | |||
1561 | int i; | 1561 | int i; |
1562 | int slot; | 1562 | int slot; |
1563 | int ret; | 1563 | int ret; |
1564 | struct btrfs_root_item *root_item = &root->root_item; | ||
1565 | |||
1564 | for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { | 1566 | for(i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) { |
1565 | slot = path->slots[i]; | 1567 | slot = path->slots[i]; |
1566 | if (slot < btrfs_header_nritems( | 1568 | if (slot < btrfs_header_nritems( |
1567 | btrfs_buffer_header(path->nodes[i])) - 1) { | 1569 | btrfs_buffer_header(path->nodes[i])) - 1) { |
1570 | struct btrfs_node *node; | ||
1571 | node = btrfs_buffer_node(path->nodes[i]); | ||
1568 | path->slots[i]++; | 1572 | path->slots[i]++; |
1569 | *level = i; | 1573 | *level = i; |
1574 | WARN_ON(*level == 0); | ||
1575 | memcpy(&root_item->drop_progress, | ||
1576 | &node->ptrs[path->slots[i]].key, | ||
1577 | sizeof(root_item->drop_progress)); | ||
1578 | root_item->drop_level = i; | ||
1570 | return 0; | 1579 | return 0; |
1571 | } else { | 1580 | } else { |
1572 | ret = btrfs_free_extent(trans, root, | 1581 | ret = btrfs_free_extent(trans, root, |
@@ -1587,7 +1596,7 @@ static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1587 | * decremented. | 1596 | * decremented. |
1588 | */ | 1597 | */ |
1589 | int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root | 1598 | int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root |
1590 | *root, struct buffer_head *snap) | 1599 | *root) |
1591 | { | 1600 | { |
1592 | int ret = 0; | 1601 | int ret = 0; |
1593 | int wret; | 1602 | int wret; |
@@ -1595,14 +1604,33 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1595 | struct btrfs_path *path; | 1604 | struct btrfs_path *path; |
1596 | int i; | 1605 | int i; |
1597 | int orig_level; | 1606 | int orig_level; |
1607 | int num_walks = 0; | ||
1608 | struct btrfs_root_item *root_item = &root->root_item; | ||
1598 | 1609 | ||
1599 | path = btrfs_alloc_path(); | 1610 | path = btrfs_alloc_path(); |
1600 | BUG_ON(!path); | 1611 | BUG_ON(!path); |
1601 | 1612 | ||
1602 | level = btrfs_header_level(btrfs_buffer_header(snap)); | 1613 | level = btrfs_header_level(btrfs_buffer_header(root->node)); |
1603 | orig_level = level; | 1614 | orig_level = level; |
1604 | path->nodes[level] = snap; | 1615 | if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { |
1605 | path->slots[level] = 0; | 1616 | path->nodes[level] = root->node; |
1617 | path->slots[level] = 0; | ||
1618 | } else { | ||
1619 | struct btrfs_key key; | ||
1620 | struct btrfs_disk_key *found_key; | ||
1621 | struct btrfs_node *node; | ||
1622 | btrfs_disk_key_to_cpu(&key, &root_item->drop_progress); | ||
1623 | wret = btrfs_search_slot(NULL, root, &key, path, 0, 0); | ||
1624 | if (ret < 0) { | ||
1625 | ret = wret; | ||
1626 | goto out; | ||
1627 | } | ||
1628 | level = root_item->drop_level; | ||
1629 | node = btrfs_buffer_node(path->nodes[level]); | ||
1630 | found_key = &node->ptrs[path->slots[level]].key; | ||
1631 | WARN_ON(memcmp(found_key, &root_item->drop_progress, | ||
1632 | sizeof(*found_key))); | ||
1633 | } | ||
1606 | while(1) { | 1634 | while(1) { |
1607 | wret = walk_down_tree(trans, root, path, &level); | 1635 | wret = walk_down_tree(trans, root, path, &level); |
1608 | if (wret > 0) | 1636 | if (wret > 0) |
@@ -1615,12 +1643,21 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1615 | break; | 1643 | break; |
1616 | if (wret < 0) | 1644 | if (wret < 0) |
1617 | ret = wret; | 1645 | ret = wret; |
1646 | num_walks++; | ||
1647 | if (num_walks > 10) { | ||
1648 | struct btrfs_key key; | ||
1649 | btrfs_disk_key_to_cpu(&key, &root_item->drop_progress); | ||
1650 | ret = -EAGAIN; | ||
1651 | get_bh(root->node); | ||
1652 | break; | ||
1653 | } | ||
1618 | } | 1654 | } |
1619 | for (i = 0; i <= orig_level; i++) { | 1655 | for (i = 0; i <= orig_level; i++) { |
1620 | if (path->nodes[i]) { | 1656 | if (path->nodes[i]) { |
1621 | btrfs_block_release(root, path->nodes[i]); | 1657 | btrfs_block_release(root, path->nodes[i]); |
1622 | } | 1658 | } |
1623 | } | 1659 | } |
1660 | out: | ||
1624 | btrfs_free_path(path); | 1661 | btrfs_free_path(path); |
1625 | return ret; | 1662 | return ret; |
1626 | } | 1663 | } |