aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2007-08-07 15:52:19 -0400
committerDavid Woodhouse <dwmw2@hera.kernel.org>2007-08-07 15:52:19 -0400
commit9f3a742736cecda5a8778be70faa2f779458839f (patch)
tree500ef3701521e63254dbe82e5c71e7ccff746275 /fs
parent8578f0f1fda41f8429a1037022b720275102ca65 (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.c10
-rw-r--r--fs/btrfs/ctree.h4
-rw-r--r--fs/btrfs/extent-tree.c45
-rw-r--r--fs/btrfs/transaction.c63
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
1073int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path); 1073int btrfs_next_leaf(struct btrfs_root *root, struct btrfs_path *path);
1074int btrfs_leaf_free_space(struct btrfs_root *root, struct btrfs_leaf *leaf); 1074int btrfs_leaf_free_space(struct btrfs_root *root, struct btrfs_leaf *leaf);
1075int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root 1075int 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 */
1078int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, 1078int 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 */
1589int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root 1598int 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 }
1660out:
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
214struct dirty_root { 214struct 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
222int btrfs_add_dead_root(struct btrfs_root *root, struct list_head *dead_list) 219int 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);