diff options
| -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 | } |
