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 | |
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')
-rw-r--r-- | fs/btrfs/ctree.c | 10 | ||||
-rw-r--r-- | fs/btrfs/ctree.h | 4 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 45 | ||||
-rw-r--r-- | fs/btrfs/transaction.c | 63 |
4 files changed, 91 insertions, 31 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 19a30b7c3a28..aa824e2c521f 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -659,9 +659,16 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
659 | struct buffer_head *b; | 659 | struct buffer_head *b; |
660 | struct buffer_head *cow_buf; | 660 | struct buffer_head *cow_buf; |
661 | struct btrfs_node *c; | 661 | struct btrfs_node *c; |
662 | struct btrfs_root_item *root_item = &root->root_item; | ||
662 | int slot; | 663 | int slot; |
663 | int ret; | 664 | int ret; |
664 | int level; | 665 | int level; |
666 | u8 lowest_level = 0; | ||
667 | |||
668 | if (btrfs_root_refs(root_item) == 0 && root->ref_cows) { | ||
669 | lowest_level = root_item->drop_level; | ||
670 | WARN_ON(ins_len || cow); | ||
671 | } | ||
665 | 672 | ||
666 | WARN_ON(p->nodes[0] != NULL); | 673 | WARN_ON(p->nodes[0] != NULL); |
667 | WARN_ON(!mutex_is_locked(&root->fs_info->fs_mutex)); | 674 | WARN_ON(!mutex_is_locked(&root->fs_info->fs_mutex)); |
@@ -718,6 +725,9 @@ again: | |||
718 | slot = p->slots[level]; | 725 | slot = p->slots[level]; |
719 | BUG_ON(btrfs_header_nritems(&c->header) == 1); | 726 | BUG_ON(btrfs_header_nritems(&c->header) == 1); |
720 | } | 727 | } |
728 | /* this is only true while dropping a snapshot */ | ||
729 | if (level == lowest_level) | ||
730 | break; | ||
721 | b = read_tree_block(root, btrfs_node_blockptr(c, slot)); | 731 | b = read_tree_block(root, btrfs_node_blockptr(c, slot)); |
722 | } else { | 732 | } else { |
723 | struct btrfs_leaf *l = (struct btrfs_leaf *)c; | 733 | struct btrfs_leaf *l = (struct btrfs_leaf *)c; |
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 0287bd51d87c..73c2e75a136d 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -333,10 +333,10 @@ struct btrfs_root { | |||
333 | u64 objectid; | 333 | u64 objectid; |
334 | u64 last_trans; | 334 | u64 last_trans; |
335 | u32 blocksize; | 335 | u32 blocksize; |
336 | int ref_cows; | ||
337 | u32 type; | 336 | u32 type; |
338 | u64 highest_inode; | 337 | u64 highest_inode; |
339 | u64 last_inode_alloc; | 338 | u64 last_inode_alloc; |
339 | int ref_cows; | ||
340 | }; | 340 | }; |
341 | 341 | ||
342 | /* the lower bits in the key flags defines the item type */ | 342 | /* the lower bits in the key flags defines the item type */ |
@@ -1073,7 +1073,7 @@ int btrfs_insert_empty_item(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1073 | int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path); | 1073 | int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path); |
1074 | int btrfs_leaf_free_space(struct btrfs_root *root, struct btrfs_leaf *leaf); | 1074 | int btrfs_leaf_free_space(struct btrfs_root *root, struct btrfs_leaf *leaf); |
1075 | int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root | 1075 | int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root |
1076 | *root, struct buffer_head *snap); | 1076 | *root); |
1077 | /* root-item.c */ | 1077 | /* root-item.c */ |
1078 | int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, | 1078 | int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, |
1079 | struct btrfs_key *key); | 1079 | struct btrfs_key *key); |
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 | |||
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 | } |
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 0f494fe365cb..498626470a04 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -213,10 +213,7 @@ static int wait_for_commit(struct btrfs_root *root, | |||
213 | 213 | ||
214 | struct dirty_root { | 214 | struct dirty_root { |
215 | struct list_head list; | 215 | struct list_head list; |
216 | struct btrfs_key snap_key; | ||
217 | struct buffer_head *commit_root; | ||
218 | struct btrfs_root *root; | 216 | struct btrfs_root *root; |
219 | int free_on_drop; | ||
220 | }; | 217 | }; |
221 | 218 | ||
222 | int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list) | 219 | int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list) |
@@ -226,10 +223,7 @@ int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list) | |||
226 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); | 223 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); |
227 | if (!dirty) | 224 | if (!dirty) |
228 | return -ENOMEM; | 225 | return -ENOMEM; |
229 | memcpy(&dirty->snap_key, &root->root_key, sizeof(root->root_key)); | ||
230 | dirty->commit_root = root->node; | ||
231 | dirty->root = root; | 226 | dirty->root = root; |
232 | dirty->free_on_drop = 1; | ||
233 | list_add(&dirty->list, dead_list); | 227 | list_add(&dirty->list, dead_list); |
234 | return 0; | 228 | return 0; |
235 | } | 229 | } |
@@ -241,7 +235,6 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
241 | struct dirty_root *dirty; | 235 | struct dirty_root *dirty; |
242 | struct btrfs_root *gang[8]; | 236 | struct btrfs_root *gang[8]; |
243 | struct btrfs_root *root; | 237 | struct btrfs_root *root; |
244 | struct btrfs_root_item tmp_item; | ||
245 | int i; | 238 | int i; |
246 | int ret; | 239 | int ret; |
247 | int err = 0; | 240 | int err = 0; |
@@ -267,13 +260,16 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
267 | } | 260 | } |
268 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); | 261 | dirty = kmalloc(sizeof(*dirty), GFP_NOFS); |
269 | BUG_ON(!dirty); | 262 | BUG_ON(!dirty); |
270 | memcpy(&dirty->snap_key, &root->root_key, | 263 | dirty->root = kmalloc(sizeof(*dirty->root), GFP_NOFS); |
271 | sizeof(root->root_key)); | 264 | BUG_ON(!dirty->root); |
272 | dirty->commit_root = root->commit_root; | 265 | |
266 | memset(&root->root_item.drop_progress, 0, | ||
267 | sizeof(struct btrfs_disk_key)); | ||
268 | root->root_item.drop_level = 0; | ||
269 | |||
270 | memcpy(dirty->root, root, sizeof(*root)); | ||
271 | dirty->root->node = root->commit_root; | ||
273 | root->commit_root = NULL; | 272 | root->commit_root = NULL; |
274 | dirty->root = root; | ||
275 | dirty->free_on_drop = 0; | ||
276 | memcpy(&tmp_item, &root->root_item, sizeof(tmp_item)); | ||
277 | 273 | ||
278 | root->root_key.offset = root->fs_info->generation; | 274 | root->root_key.offset = root->fs_info->generation; |
279 | btrfs_set_root_blocknr(&root->root_item, | 275 | btrfs_set_root_blocknr(&root->root_item, |
@@ -283,17 +279,21 @@ static int add_dirty_roots(struct btrfs_trans_handle *trans, | |||
283 | &root->root_item); | 279 | &root->root_item); |
284 | if (err) | 280 | if (err) |
285 | break; | 281 | break; |
286 | refs = btrfs_root_refs(&tmp_item); | 282 | |
287 | btrfs_set_root_refs(&tmp_item, refs - 1); | 283 | refs = btrfs_root_refs(&dirty->root->root_item); |
284 | btrfs_set_root_refs(&dirty->root->root_item, refs - 1); | ||
288 | err = btrfs_update_root(trans, root->fs_info->tree_root, | 285 | err = btrfs_update_root(trans, root->fs_info->tree_root, |
289 | &dirty->snap_key, | 286 | &dirty->root->root_key, |
290 | &tmp_item); | 287 | &dirty->root->root_item); |
291 | 288 | ||
292 | BUG_ON(err); | 289 | BUG_ON(err); |
293 | if (refs == 1) | 290 | if (refs == 1) { |
294 | list_add(&dirty->list, list); | 291 | list_add(&dirty->list, list); |
295 | else | 292 | } else { |
293 | WARN_ON(1); | ||
294 | kfree(dirty->root); | ||
296 | kfree(dirty); | 295 | kfree(dirty); |
296 | } | ||
297 | } | 297 | } |
298 | } | 298 | } |
299 | return err; | 299 | return err; |
@@ -305,23 +305,36 @@ static int drop_dirty_roots(struct btrfs_root *tree_root, | |||
305 | struct dirty_root *dirty; | 305 | struct dirty_root *dirty; |
306 | struct btrfs_trans_handle *trans; | 306 | struct btrfs_trans_handle *trans; |
307 | int ret = 0; | 307 | int ret = 0; |
308 | int err; | ||
309 | |||
308 | while(!list_empty(list)) { | 310 | while(!list_empty(list)) { |
309 | mutex_lock(&tree_root->fs_info->fs_mutex); | 311 | mutex_lock(&tree_root->fs_info->fs_mutex); |
310 | dirty = list_entry(list->next, struct dirty_root, list); | 312 | dirty = list_entry(list->next, struct dirty_root, list); |
311 | list_del_init(&dirty->list); | 313 | list_del_init(&dirty->list); |
312 | 314 | ||
313 | trans = btrfs_start_transaction(tree_root, 1); | 315 | while(1) { |
314 | ret = btrfs_drop_snapshot(trans, dirty->root, | 316 | trans = btrfs_start_transaction(tree_root, 1); |
315 | dirty->commit_root); | 317 | ret = btrfs_drop_snapshot(trans, dirty->root); |
318 | if (ret != -EAGAIN) { | ||
319 | break; | ||
320 | } | ||
321 | err = btrfs_update_root(trans, | ||
322 | tree_root, | ||
323 | &dirty->root->root_key, | ||
324 | &dirty->root->root_item); | ||
325 | if (err) | ||
326 | ret = err; | ||
327 | ret = btrfs_end_transaction(trans, tree_root); | ||
328 | BUG_ON(ret); | ||
329 | } | ||
316 | BUG_ON(ret); | 330 | BUG_ON(ret); |
317 | ret = btrfs_del_root(trans, tree_root, &dirty->snap_key); | 331 | ret = btrfs_del_root(trans, tree_root, &dirty->root->root_key); |
318 | if (ret) | 332 | if (ret) |
319 | break; | 333 | break; |
320 | ret = btrfs_end_transaction(trans, tree_root); | 334 | ret = btrfs_end_transaction(trans, tree_root); |
321 | BUG_ON(ret); | 335 | BUG_ON(ret); |
322 | 336 | ||
323 | if (dirty->free_on_drop) | 337 | kfree(dirty->root); |
324 | kfree(dirty->root); | ||
325 | kfree(dirty); | 338 | kfree(dirty); |
326 | mutex_unlock(&tree_root->fs_info->fs_mutex); | 339 | mutex_unlock(&tree_root->fs_info->fs_mutex); |
327 | btrfs_btree_balance_dirty(tree_root); | 340 | btrfs_btree_balance_dirty(tree_root); |