diff options
author | Chris Mason <chris.mason@oracle.com> | 2009-03-13 10:24:59 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2009-03-24 16:14:25 -0400 |
commit | 9fa8cfe706f9c20067c042a064999d5825a35330 (patch) | |
tree | 8e64281caffcdb3ad5a8ab8bf22bb90483e9e214 | |
parent | 8e0ee43bc2c3e19db56a4adaa9a9b04ce885cd84 (diff) |
Btrfs: don't preallocate metadata blocks during btrfs_search_slot
In order to avoid doing expensive extent management with tree locks held,
btrfs_search_slot will preallocate tree blocks for use by COW without
any tree locks held.
A later commit moves all of the extent allocation work for COW into
a delayed update mechanism, and this preallocation will no longer be
required.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r-- | fs/btrfs/ctree.c | 100 | ||||
-rw-r--r-- | fs/btrfs/ctree.h | 2 | ||||
-rw-r--r-- | fs/btrfs/transaction.c | 4 |
3 files changed, 21 insertions, 85 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 37f31b5529aa..87c90387283b 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -254,18 +254,13 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans, | |||
254 | * empty_size -- a hint that you plan on doing more cow. This is the size in | 254 | * empty_size -- a hint that you plan on doing more cow. This is the size in |
255 | * bytes the allocator should try to find free next to the block it returns. | 255 | * bytes the allocator should try to find free next to the block it returns. |
256 | * This is just a hint and may be ignored by the allocator. | 256 | * This is just a hint and may be ignored by the allocator. |
257 | * | ||
258 | * prealloc_dest -- if you have already reserved a destination for the cow, | ||
259 | * this uses that block instead of allocating a new one. | ||
260 | * btrfs_alloc_reserved_extent is used to finish the allocation. | ||
261 | */ | 257 | */ |
262 | static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, | 258 | static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, |
263 | struct btrfs_root *root, | 259 | struct btrfs_root *root, |
264 | struct extent_buffer *buf, | 260 | struct extent_buffer *buf, |
265 | struct extent_buffer *parent, int parent_slot, | 261 | struct extent_buffer *parent, int parent_slot, |
266 | struct extent_buffer **cow_ret, | 262 | struct extent_buffer **cow_ret, |
267 | u64 search_start, u64 empty_size, | 263 | u64 search_start, u64 empty_size) |
268 | u64 prealloc_dest) | ||
269 | { | 264 | { |
270 | u64 parent_start; | 265 | u64 parent_start; |
271 | struct extent_buffer *cow; | 266 | struct extent_buffer *cow; |
@@ -291,26 +286,10 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, | |||
291 | level = btrfs_header_level(buf); | 286 | level = btrfs_header_level(buf); |
292 | nritems = btrfs_header_nritems(buf); | 287 | nritems = btrfs_header_nritems(buf); |
293 | 288 | ||
294 | if (prealloc_dest) { | 289 | cow = btrfs_alloc_free_block(trans, root, buf->len, |
295 | struct btrfs_key ins; | 290 | parent_start, root->root_key.objectid, |
296 | 291 | trans->transid, level, | |
297 | ins.objectid = prealloc_dest; | 292 | search_start, empty_size); |
298 | ins.offset = buf->len; | ||
299 | ins.type = BTRFS_EXTENT_ITEM_KEY; | ||
300 | |||
301 | ret = btrfs_alloc_reserved_extent(trans, root, parent_start, | ||
302 | root->root_key.objectid, | ||
303 | trans->transid, level, &ins); | ||
304 | BUG_ON(ret); | ||
305 | cow = btrfs_init_new_buffer(trans, root, prealloc_dest, | ||
306 | buf->len, level); | ||
307 | } else { | ||
308 | cow = btrfs_alloc_free_block(trans, root, buf->len, | ||
309 | parent_start, | ||
310 | root->root_key.objectid, | ||
311 | trans->transid, level, | ||
312 | search_start, empty_size); | ||
313 | } | ||
314 | if (IS_ERR(cow)) | 293 | if (IS_ERR(cow)) |
315 | return PTR_ERR(cow); | 294 | return PTR_ERR(cow); |
316 | 295 | ||
@@ -413,7 +392,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, | |||
413 | noinline int btrfs_cow_block(struct btrfs_trans_handle *trans, | 392 | noinline int btrfs_cow_block(struct btrfs_trans_handle *trans, |
414 | struct btrfs_root *root, struct extent_buffer *buf, | 393 | struct btrfs_root *root, struct extent_buffer *buf, |
415 | struct extent_buffer *parent, int parent_slot, | 394 | struct extent_buffer *parent, int parent_slot, |
416 | struct extent_buffer **cow_ret, u64 prealloc_dest) | 395 | struct extent_buffer **cow_ret) |
417 | { | 396 | { |
418 | u64 search_start; | 397 | u64 search_start; |
419 | int ret; | 398 | int ret; |
@@ -436,7 +415,6 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans, | |||
436 | btrfs_header_owner(buf) == root->root_key.objectid && | 415 | btrfs_header_owner(buf) == root->root_key.objectid && |
437 | !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) { | 416 | !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) { |
438 | *cow_ret = buf; | 417 | *cow_ret = buf; |
439 | WARN_ON(prealloc_dest); | ||
440 | return 0; | 418 | return 0; |
441 | } | 419 | } |
442 | 420 | ||
@@ -447,8 +425,7 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans, | |||
447 | btrfs_set_lock_blocking(buf); | 425 | btrfs_set_lock_blocking(buf); |
448 | 426 | ||
449 | ret = __btrfs_cow_block(trans, root, buf, parent, | 427 | ret = __btrfs_cow_block(trans, root, buf, parent, |
450 | parent_slot, cow_ret, search_start, 0, | 428 | parent_slot, cow_ret, search_start, 0); |
451 | prealloc_dest); | ||
452 | return ret; | 429 | return ret; |
453 | } | 430 | } |
454 | 431 | ||
@@ -617,7 +594,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, | |||
617 | err = __btrfs_cow_block(trans, root, cur, parent, i, | 594 | err = __btrfs_cow_block(trans, root, cur, parent, i, |
618 | &cur, search_start, | 595 | &cur, search_start, |
619 | min(16 * blocksize, | 596 | min(16 * blocksize, |
620 | (end_slot - i) * blocksize), 0); | 597 | (end_slot - i) * blocksize)); |
621 | if (err) { | 598 | if (err) { |
622 | btrfs_tree_unlock(cur); | 599 | btrfs_tree_unlock(cur); |
623 | free_extent_buffer(cur); | 600 | free_extent_buffer(cur); |
@@ -937,7 +914,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, | |||
937 | BUG_ON(!child); | 914 | BUG_ON(!child); |
938 | btrfs_tree_lock(child); | 915 | btrfs_tree_lock(child); |
939 | btrfs_set_lock_blocking(child); | 916 | btrfs_set_lock_blocking(child); |
940 | ret = btrfs_cow_block(trans, root, child, mid, 0, &child, 0); | 917 | ret = btrfs_cow_block(trans, root, child, mid, 0, &child); |
941 | BUG_ON(ret); | 918 | BUG_ON(ret); |
942 | 919 | ||
943 | spin_lock(&root->node_lock); | 920 | spin_lock(&root->node_lock); |
@@ -979,7 +956,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, | |||
979 | btrfs_tree_lock(left); | 956 | btrfs_tree_lock(left); |
980 | btrfs_set_lock_blocking(left); | 957 | btrfs_set_lock_blocking(left); |
981 | wret = btrfs_cow_block(trans, root, left, | 958 | wret = btrfs_cow_block(trans, root, left, |
982 | parent, pslot - 1, &left, 0); | 959 | parent, pslot - 1, &left); |
983 | if (wret) { | 960 | if (wret) { |
984 | ret = wret; | 961 | ret = wret; |
985 | goto enospc; | 962 | goto enospc; |
@@ -990,7 +967,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, | |||
990 | btrfs_tree_lock(right); | 967 | btrfs_tree_lock(right); |
991 | btrfs_set_lock_blocking(right); | 968 | btrfs_set_lock_blocking(right); |
992 | wret = btrfs_cow_block(trans, root, right, | 969 | wret = btrfs_cow_block(trans, root, right, |
993 | parent, pslot + 1, &right, 0); | 970 | parent, pslot + 1, &right); |
994 | if (wret) { | 971 | if (wret) { |
995 | ret = wret; | 972 | ret = wret; |
996 | goto enospc; | 973 | goto enospc; |
@@ -1171,7 +1148,7 @@ static noinline int push_nodes_for_insert(struct btrfs_trans_handle *trans, | |||
1171 | wret = 1; | 1148 | wret = 1; |
1172 | } else { | 1149 | } else { |
1173 | ret = btrfs_cow_block(trans, root, left, parent, | 1150 | ret = btrfs_cow_block(trans, root, left, parent, |
1174 | pslot - 1, &left, 0); | 1151 | pslot - 1, &left); |
1175 | if (ret) | 1152 | if (ret) |
1176 | wret = 1; | 1153 | wret = 1; |
1177 | else { | 1154 | else { |
@@ -1222,7 +1199,7 @@ static noinline int push_nodes_for_insert(struct btrfs_trans_handle *trans, | |||
1222 | } else { | 1199 | } else { |
1223 | ret = btrfs_cow_block(trans, root, right, | 1200 | ret = btrfs_cow_block(trans, root, right, |
1224 | parent, pslot + 1, | 1201 | parent, pslot + 1, |
1225 | &right, 0); | 1202 | &right); |
1226 | if (ret) | 1203 | if (ret) |
1227 | wret = 1; | 1204 | wret = 1; |
1228 | else { | 1205 | else { |
@@ -1492,7 +1469,6 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1492 | u8 lowest_level = 0; | 1469 | u8 lowest_level = 0; |
1493 | u64 blocknr; | 1470 | u64 blocknr; |
1494 | u64 gen; | 1471 | u64 gen; |
1495 | struct btrfs_key prealloc_block; | ||
1496 | 1472 | ||
1497 | lowest_level = p->lowest_level; | 1473 | lowest_level = p->lowest_level; |
1498 | WARN_ON(lowest_level && ins_len > 0); | 1474 | WARN_ON(lowest_level && ins_len > 0); |
@@ -1501,8 +1477,6 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1501 | if (ins_len < 0) | 1477 | if (ins_len < 0) |
1502 | lowest_unlock = 2; | 1478 | lowest_unlock = 2; |
1503 | 1479 | ||
1504 | prealloc_block.objectid = 0; | ||
1505 | |||
1506 | again: | 1480 | again: |
1507 | if (p->skip_locking) | 1481 | if (p->skip_locking) |
1508 | b = btrfs_root_node(root); | 1482 | b = btrfs_root_node(root); |
@@ -1529,44 +1503,11 @@ again: | |||
1529 | !btrfs_header_flag(b, BTRFS_HEADER_FLAG_WRITTEN)) { | 1503 | !btrfs_header_flag(b, BTRFS_HEADER_FLAG_WRITTEN)) { |
1530 | goto cow_done; | 1504 | goto cow_done; |
1531 | } | 1505 | } |
1532 | |||
1533 | /* ok, we have to cow, is our old prealloc the right | ||
1534 | * size? | ||
1535 | */ | ||
1536 | if (prealloc_block.objectid && | ||
1537 | prealloc_block.offset != b->len) { | ||
1538 | btrfs_release_path(root, p); | ||
1539 | btrfs_free_reserved_extent(root, | ||
1540 | prealloc_block.objectid, | ||
1541 | prealloc_block.offset); | ||
1542 | prealloc_block.objectid = 0; | ||
1543 | goto again; | ||
1544 | } | ||
1545 | |||
1546 | /* | ||
1547 | * for higher level blocks, try not to allocate blocks | ||
1548 | * with the block and the parent locks held. | ||
1549 | */ | ||
1550 | if (level > 0 && !prealloc_block.objectid) { | ||
1551 | u32 size = b->len; | ||
1552 | u64 hint = b->start; | ||
1553 | |||
1554 | btrfs_release_path(root, p); | ||
1555 | ret = btrfs_reserve_extent(trans, root, | ||
1556 | size, size, 0, | ||
1557 | hint, (u64)-1, | ||
1558 | &prealloc_block, 0); | ||
1559 | BUG_ON(ret); | ||
1560 | goto again; | ||
1561 | } | ||
1562 | |||
1563 | btrfs_set_path_blocking(p); | 1506 | btrfs_set_path_blocking(p); |
1564 | 1507 | ||
1565 | wret = btrfs_cow_block(trans, root, b, | 1508 | wret = btrfs_cow_block(trans, root, b, |
1566 | p->nodes[level + 1], | 1509 | p->nodes[level + 1], |
1567 | p->slots[level + 1], | 1510 | p->slots[level + 1], &b); |
1568 | &b, prealloc_block.objectid); | ||
1569 | prealloc_block.objectid = 0; | ||
1570 | if (wret) { | 1511 | if (wret) { |
1571 | free_extent_buffer(b); | 1512 | free_extent_buffer(b); |
1572 | ret = wret; | 1513 | ret = wret; |
@@ -1743,11 +1684,6 @@ done: | |||
1743 | * from here on, so for now just mark it as blocking | 1684 | * from here on, so for now just mark it as blocking |
1744 | */ | 1685 | */ |
1745 | btrfs_set_path_blocking(p); | 1686 | btrfs_set_path_blocking(p); |
1746 | if (prealloc_block.objectid) { | ||
1747 | btrfs_free_reserved_extent(root, | ||
1748 | prealloc_block.objectid, | ||
1749 | prealloc_block.offset); | ||
1750 | } | ||
1751 | return ret; | 1687 | return ret; |
1752 | } | 1688 | } |
1753 | 1689 | ||
@@ -1768,7 +1704,7 @@ int btrfs_merge_path(struct btrfs_trans_handle *trans, | |||
1768 | int ret; | 1704 | int ret; |
1769 | 1705 | ||
1770 | eb = btrfs_lock_root_node(root); | 1706 | eb = btrfs_lock_root_node(root); |
1771 | ret = btrfs_cow_block(trans, root, eb, NULL, 0, &eb, 0); | 1707 | ret = btrfs_cow_block(trans, root, eb, NULL, 0, &eb); |
1772 | BUG_ON(ret); | 1708 | BUG_ON(ret); |
1773 | 1709 | ||
1774 | btrfs_set_lock_blocking(eb); | 1710 | btrfs_set_lock_blocking(eb); |
@@ -1826,7 +1762,7 @@ int btrfs_merge_path(struct btrfs_trans_handle *trans, | |||
1826 | } | 1762 | } |
1827 | 1763 | ||
1828 | ret = btrfs_cow_block(trans, root, eb, parent, slot, | 1764 | ret = btrfs_cow_block(trans, root, eb, parent, slot, |
1829 | &eb, 0); | 1765 | &eb); |
1830 | BUG_ON(ret); | 1766 | BUG_ON(ret); |
1831 | 1767 | ||
1832 | if (root->root_key.objectid == | 1768 | if (root->root_key.objectid == |
@@ -2377,7 +2313,7 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root | |||
2377 | 2313 | ||
2378 | /* cow and double check */ | 2314 | /* cow and double check */ |
2379 | ret = btrfs_cow_block(trans, root, right, upper, | 2315 | ret = btrfs_cow_block(trans, root, right, upper, |
2380 | slot + 1, &right, 0); | 2316 | slot + 1, &right); |
2381 | if (ret) | 2317 | if (ret) |
2382 | goto out_unlock; | 2318 | goto out_unlock; |
2383 | 2319 | ||
@@ -2576,7 +2512,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root | |||
2576 | 2512 | ||
2577 | /* cow and double check */ | 2513 | /* cow and double check */ |
2578 | ret = btrfs_cow_block(trans, root, left, | 2514 | ret = btrfs_cow_block(trans, root, left, |
2579 | path->nodes[1], slot - 1, &left, 0); | 2515 | path->nodes[1], slot - 1, &left); |
2580 | if (ret) { | 2516 | if (ret) { |
2581 | /* we hit -ENOSPC, but it isn't fatal here */ | 2517 | /* we hit -ENOSPC, but it isn't fatal here */ |
2582 | ret = 1; | 2518 | ret = 1; |
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 5e1d4e30e9d8..3a37ba7a8d65 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1838,7 +1838,7 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key, | |||
1838 | int btrfs_cow_block(struct btrfs_trans_handle *trans, | 1838 | int btrfs_cow_block(struct btrfs_trans_handle *trans, |
1839 | struct btrfs_root *root, struct extent_buffer *buf, | 1839 | struct btrfs_root *root, struct extent_buffer *buf, |
1840 | struct extent_buffer *parent, int parent_slot, | 1840 | struct extent_buffer *parent, int parent_slot, |
1841 | struct extent_buffer **cow_ret, u64 prealloc_dest); | 1841 | struct extent_buffer **cow_ret); |
1842 | int btrfs_copy_root(struct btrfs_trans_handle *trans, | 1842 | int btrfs_copy_root(struct btrfs_trans_handle *trans, |
1843 | struct btrfs_root *root, | 1843 | struct btrfs_root *root, |
1844 | struct extent_buffer *buf, | 1844 | struct extent_buffer *buf, |
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 4112d53d4f4d..d638c54d39e9 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -463,7 +463,7 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans, | |||
463 | btrfs_extent_post_op(trans, fs_info->tree_root); | 463 | btrfs_extent_post_op(trans, fs_info->tree_root); |
464 | 464 | ||
465 | eb = btrfs_lock_root_node(fs_info->tree_root); | 465 | eb = btrfs_lock_root_node(fs_info->tree_root); |
466 | btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb, 0); | 466 | btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); |
467 | btrfs_tree_unlock(eb); | 467 | btrfs_tree_unlock(eb); |
468 | free_extent_buffer(eb); | 468 | free_extent_buffer(eb); |
469 | 469 | ||
@@ -766,7 +766,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, | |||
766 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); | 766 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); |
767 | 767 | ||
768 | old = btrfs_lock_root_node(root); | 768 | old = btrfs_lock_root_node(root); |
769 | btrfs_cow_block(trans, root, old, NULL, 0, &old, 0); | 769 | btrfs_cow_block(trans, root, old, NULL, 0, &old); |
770 | 770 | ||
771 | btrfs_copy_root(trans, root, old, &tmp, objectid); | 771 | btrfs_copy_root(trans, root, old, &tmp, objectid); |
772 | btrfs_tree_unlock(old); | 772 | btrfs_tree_unlock(old); |