diff options
Diffstat (limited to 'fs/btrfs/extent-tree.c')
-rw-r--r-- | fs/btrfs/extent-tree.c | 85 |
1 files changed, 79 insertions, 6 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 3b26f0980946..7a22f2e6ec47 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/pagemap.h> | 19 | #include <linux/pagemap.h> |
20 | #include <linux/writeback.h> | 20 | #include <linux/writeback.h> |
21 | #include <linux/blkdev.h> | 21 | #include <linux/blkdev.h> |
22 | #include <linux/sort.h> | ||
22 | #include "compat.h" | 23 | #include "compat.h" |
23 | #include "hash.h" | 24 | #include "hash.h" |
24 | #include "crc32c.h" | 25 | #include "crc32c.h" |
@@ -1521,15 +1522,50 @@ out: | |||
1521 | return ret; | 1522 | return ret; |
1522 | } | 1523 | } |
1523 | 1524 | ||
1524 | int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | 1525 | /* when a block goes through cow, we update the reference counts of |
1525 | struct extent_buffer *orig_buf, struct extent_buffer *buf, | 1526 | * everything that block points to. The internal pointers of the block |
1526 | u32 *nr_extents) | 1527 | * can be in just about any order, and it is likely to have clusters of |
1528 | * things that are close together and clusters of things that are not. | ||
1529 | * | ||
1530 | * To help reduce the seeks that come with updating all of these reference | ||
1531 | * counts, sort them by byte number before actual updates are done. | ||
1532 | * | ||
1533 | * struct refsort is used to match byte number to slot in the btree block. | ||
1534 | * we sort based on the byte number and then use the slot to actually | ||
1535 | * find the item. | ||
1536 | */ | ||
1537 | struct refsort { | ||
1538 | u64 bytenr; | ||
1539 | u32 slot; | ||
1540 | }; | ||
1541 | |||
1542 | /* | ||
1543 | * for passing into sort() | ||
1544 | */ | ||
1545 | static int refsort_cmp(const void *a_void, const void *b_void) | ||
1546 | { | ||
1547 | const struct refsort *a = a_void; | ||
1548 | const struct refsort *b = b_void; | ||
1549 | |||
1550 | if (a->bytenr < b->bytenr) | ||
1551 | return -1; | ||
1552 | if (a->bytenr > b->bytenr) | ||
1553 | return 1; | ||
1554 | return 0; | ||
1555 | } | ||
1556 | |||
1557 | |||
1558 | noinline int btrfs_inc_ref(struct btrfs_trans_handle *trans, | ||
1559 | struct btrfs_root *root, | ||
1560 | struct extent_buffer *orig_buf, | ||
1561 | struct extent_buffer *buf, u32 *nr_extents) | ||
1527 | { | 1562 | { |
1528 | u64 bytenr; | 1563 | u64 bytenr; |
1529 | u64 ref_root; | 1564 | u64 ref_root; |
1530 | u64 orig_root; | 1565 | u64 orig_root; |
1531 | u64 ref_generation; | 1566 | u64 ref_generation; |
1532 | u64 orig_generation; | 1567 | u64 orig_generation; |
1568 | struct refsort *sorted; | ||
1533 | u32 nritems; | 1569 | u32 nritems; |
1534 | u32 nr_file_extents = 0; | 1570 | u32 nr_file_extents = 0; |
1535 | struct btrfs_key key; | 1571 | struct btrfs_key key; |
@@ -1538,6 +1574,8 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
1538 | int level; | 1574 | int level; |
1539 | int ret = 0; | 1575 | int ret = 0; |
1540 | int faili = 0; | 1576 | int faili = 0; |
1577 | int refi = 0; | ||
1578 | int slot; | ||
1541 | int (*process_func)(struct btrfs_trans_handle *, struct btrfs_root *, | 1579 | int (*process_func)(struct btrfs_trans_handle *, struct btrfs_root *, |
1542 | u64, u64, u64, u64, u64, u64, u64, u64); | 1580 | u64, u64, u64, u64, u64, u64, u64, u64); |
1543 | 1581 | ||
@@ -1549,6 +1587,9 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
1549 | nritems = btrfs_header_nritems(buf); | 1587 | nritems = btrfs_header_nritems(buf); |
1550 | level = btrfs_header_level(buf); | 1588 | level = btrfs_header_level(buf); |
1551 | 1589 | ||
1590 | sorted = kmalloc(sizeof(struct refsort) * nritems, GFP_NOFS); | ||
1591 | BUG_ON(!sorted); | ||
1592 | |||
1552 | if (root->ref_cows) { | 1593 | if (root->ref_cows) { |
1553 | process_func = __btrfs_inc_extent_ref; | 1594 | process_func = __btrfs_inc_extent_ref; |
1554 | } else { | 1595 | } else { |
@@ -1561,6 +1602,11 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
1561 | process_func = __btrfs_update_extent_ref; | 1602 | process_func = __btrfs_update_extent_ref; |
1562 | } | 1603 | } |
1563 | 1604 | ||
1605 | /* | ||
1606 | * we make two passes through the items. In the first pass we | ||
1607 | * only record the byte number and slot. Then we sort based on | ||
1608 | * byte number and do the actual work based on the sorted results | ||
1609 | */ | ||
1564 | for (i = 0; i < nritems; i++) { | 1610 | for (i = 0; i < nritems; i++) { |
1565 | cond_resched(); | 1611 | cond_resched(); |
1566 | if (level == 0) { | 1612 | if (level == 0) { |
@@ -1577,6 +1623,32 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
1577 | continue; | 1623 | continue; |
1578 | 1624 | ||
1579 | nr_file_extents++; | 1625 | nr_file_extents++; |
1626 | sorted[refi].bytenr = bytenr; | ||
1627 | sorted[refi].slot = i; | ||
1628 | refi++; | ||
1629 | } else { | ||
1630 | bytenr = btrfs_node_blockptr(buf, i); | ||
1631 | sorted[refi].bytenr = bytenr; | ||
1632 | sorted[refi].slot = i; | ||
1633 | refi++; | ||
1634 | } | ||
1635 | } | ||
1636 | /* | ||
1637 | * if refi == 0, we didn't actually put anything into the sorted | ||
1638 | * array and we're done | ||
1639 | */ | ||
1640 | if (refi == 0) | ||
1641 | goto out; | ||
1642 | |||
1643 | sort(sorted, refi, sizeof(struct refsort), refsort_cmp, NULL); | ||
1644 | |||
1645 | for (i = 0; i < refi; i++) { | ||
1646 | cond_resched(); | ||
1647 | slot = sorted[i].slot; | ||
1648 | bytenr = sorted[i].bytenr; | ||
1649 | |||
1650 | if (level == 0) { | ||
1651 | btrfs_item_key_to_cpu(buf, &key, slot); | ||
1580 | 1652 | ||
1581 | ret = process_func(trans, root, bytenr, | 1653 | ret = process_func(trans, root, bytenr, |
1582 | orig_buf->start, buf->start, | 1654 | orig_buf->start, buf->start, |
@@ -1585,25 +1657,25 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
1585 | key.objectid); | 1657 | key.objectid); |
1586 | 1658 | ||
1587 | if (ret) { | 1659 | if (ret) { |
1588 | faili = i; | 1660 | faili = slot; |
1589 | WARN_ON(1); | 1661 | WARN_ON(1); |
1590 | goto fail; | 1662 | goto fail; |
1591 | } | 1663 | } |
1592 | } else { | 1664 | } else { |
1593 | bytenr = btrfs_node_blockptr(buf, i); | ||
1594 | ret = process_func(trans, root, bytenr, | 1665 | ret = process_func(trans, root, bytenr, |
1595 | orig_buf->start, buf->start, | 1666 | orig_buf->start, buf->start, |
1596 | orig_root, ref_root, | 1667 | orig_root, ref_root, |
1597 | orig_generation, ref_generation, | 1668 | orig_generation, ref_generation, |
1598 | level - 1); | 1669 | level - 1); |
1599 | if (ret) { | 1670 | if (ret) { |
1600 | faili = i; | 1671 | faili = slot; |
1601 | WARN_ON(1); | 1672 | WARN_ON(1); |
1602 | goto fail; | 1673 | goto fail; |
1603 | } | 1674 | } |
1604 | } | 1675 | } |
1605 | } | 1676 | } |
1606 | out: | 1677 | out: |
1678 | kfree(sorted); | ||
1607 | if (nr_extents) { | 1679 | if (nr_extents) { |
1608 | if (level == 0) | 1680 | if (level == 0) |
1609 | *nr_extents = nr_file_extents; | 1681 | *nr_extents = nr_file_extents; |
@@ -1612,6 +1684,7 @@ out: | |||
1612 | } | 1684 | } |
1613 | return 0; | 1685 | return 0; |
1614 | fail: | 1686 | fail: |
1687 | kfree(sorted); | ||
1615 | WARN_ON(1); | 1688 | WARN_ON(1); |
1616 | return ret; | 1689 | return ret; |
1617 | } | 1690 | } |