aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2018-09-27 02:42:30 -0400
committerDavid Sterba <dsterba@suse.com>2018-10-15 11:23:36 -0400
commit25982561db7f43f29305704f9f24ff36ea7d5671 (patch)
tree3df3c988be74ace3ed4c210a6de6a36e8d4dfda4
parentc337e7b02f71c4b2f6f2138807a284d2c4e1ac5e (diff)
btrfs: qgroup: Introduce function to trace two swaped extents
Introduce a new function, qgroup_trace_extent_swap(), which will be used later for balance qgroup speedup. The basis idea of balance is swapping tree blocks between reloc tree and the real file tree. The swap will happen in highest tree block, but there may be a lot of tree blocks involved. For example: OO = Old tree blocks NN = New tree blocks allocated during balance File tree (257) Reloc tree for 257 L2 OO NN / \ / \ L1 OO OO (a) OO NN (a) / \ / \ / \ / \ L0 OO OO OO OO OO OO NN NN (b) (c) (b) (c) When calling qgroup_trace_extent_swap(), we will pass: @src_eb = OO(a) @dst_path = [ nodes[1] = NN(a), nodes[0] = NN(c) ] @dst_level = 0 @root_level = 1 In that case, qgroup_trace_extent_swap() will search from OO(a) to reach OO(c), then mark both OO(c) and NN(c) as qgroup dirty. The main work of qgroup_trace_extent_swap() can be split into 3 parts: 1) Tree search from @src_eb It should acts as a simplified btrfs_search_slot(). The key for search can be extracted from @dst_path->nodes[dst_level] (first key). 2) Mark the final tree blocks in @src_path and @dst_path qgroup dirty NOTE: In above case, OO(a) and NN(a) won't be marked qgroup dirty. They should be marked during preivous (@dst_level = 1) iteration. 3) Mark file extents in leaves dirty We don't have good way to pick out new file extents only. So we still follow the old method by scanning all file extents in the leave. This function can free us from keeping two pathes, thus later we only need to care about how to iterate all new tree blocks in reloc tree. Signed-off-by: Qu Wenruo <wqu@suse.com> [ copy changelog to function comment ] Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/qgroup.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 8a03adc11f53..7086353153f3 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -1712,6 +1712,168 @@ static int adjust_slots_upwards(struct btrfs_path *path, int root_level)
1712 return 0; 1712 return 0;
1713} 1713}
1714 1714
1715/*
1716 * Helper function to trace a subtree tree block swap.
1717 *
1718 * The swap will happen in highest tree block, but there may be a lot of
1719 * tree blocks involved.
1720 *
1721 * For example:
1722 * OO = Old tree blocks
1723 * NN = New tree blocks allocated during balance
1724 *
1725 * File tree (257) Reloc tree for 257
1726 * L2 OO NN
1727 * / \ / \
1728 * L1 OO OO (a) OO NN (a)
1729 * / \ / \ / \ / \
1730 * L0 OO OO OO OO OO OO NN NN
1731 * (b) (c) (b) (c)
1732 *
1733 * When calling qgroup_trace_extent_swap(), we will pass:
1734 * @src_eb = OO(a)
1735 * @dst_path = [ nodes[1] = NN(a), nodes[0] = NN(c) ]
1736 * @dst_level = 0
1737 * @root_level = 1
1738 *
1739 * In that case, qgroup_trace_extent_swap() will search from OO(a) to
1740 * reach OO(c), then mark both OO(c) and NN(c) as qgroup dirty.
1741 *
1742 * The main work of qgroup_trace_extent_swap() can be split into 3 parts:
1743 *
1744 * 1) Tree search from @src_eb
1745 * It should acts as a simplified btrfs_search_slot().
1746 * The key for search can be extracted from @dst_path->nodes[dst_level]
1747 * (first key).
1748 *
1749 * 2) Mark the final tree blocks in @src_path and @dst_path qgroup dirty
1750 * NOTE: In above case, OO(a) and NN(a) won't be marked qgroup dirty.
1751 * They should be marked during preivous (@dst_level = 1) iteration.
1752 *
1753 * 3) Mark file extents in leaves dirty
1754 * We don't have good way to pick out new file extents only.
1755 * So we still follow the old method by scanning all file extents in
1756 * the leave.
1757 *
1758 * This function can free us from keeping two pathes, thus later we only need
1759 * to care about how to iterate all new tree blocks in reloc tree.
1760 */
1761static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans,
1762 struct extent_buffer *src_eb,
1763 struct btrfs_path *dst_path,
1764 int dst_level, int root_level)
1765{
1766 struct btrfs_key key;
1767 struct btrfs_path *src_path;
1768 struct btrfs_fs_info *fs_info = trans->fs_info;
1769 u32 nodesize = fs_info->nodesize;
1770 int cur_level = root_level;
1771 int ret;
1772
1773 BUG_ON(dst_level > root_level);
1774 /* Level mismatch */
1775 if (btrfs_header_level(src_eb) != root_level)
1776 return -EINVAL;
1777
1778 src_path = btrfs_alloc_path();
1779 if (!src_path) {
1780 ret = -ENOMEM;
1781 goto out;
1782 }
1783
1784 if (dst_level)
1785 btrfs_node_key_to_cpu(dst_path->nodes[dst_level], &key, 0);
1786 else
1787 btrfs_item_key_to_cpu(dst_path->nodes[dst_level], &key, 0);
1788
1789 /* For src_path */
1790 extent_buffer_get(src_eb);
1791 src_path->nodes[root_level] = src_eb;
1792 src_path->slots[root_level] = dst_path->slots[root_level];
1793 src_path->locks[root_level] = 0;
1794
1795 /* A simplified version of btrfs_search_slot() */
1796 while (cur_level >= dst_level) {
1797 struct btrfs_key src_key;
1798 struct btrfs_key dst_key;
1799
1800 if (src_path->nodes[cur_level] == NULL) {
1801 struct btrfs_key first_key;
1802 struct extent_buffer *eb;
1803 int parent_slot;
1804 u64 child_gen;
1805 u64 child_bytenr;
1806
1807 eb = src_path->nodes[cur_level + 1];
1808 parent_slot = src_path->slots[cur_level + 1];
1809 child_bytenr = btrfs_node_blockptr(eb, parent_slot);
1810 child_gen = btrfs_node_ptr_generation(eb, parent_slot);
1811 btrfs_node_key_to_cpu(eb, &first_key, parent_slot);
1812
1813 eb = read_tree_block(fs_info, child_bytenr, child_gen,
1814 cur_level, &first_key);
1815 if (IS_ERR(eb)) {
1816 ret = PTR_ERR(eb);
1817 goto out;
1818 } else if (!extent_buffer_uptodate(eb)) {
1819 free_extent_buffer(eb);
1820 ret = -EIO;
1821 goto out;
1822 }
1823
1824 src_path->nodes[cur_level] = eb;
1825
1826 btrfs_tree_read_lock(eb);
1827 btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
1828 src_path->locks[cur_level] = BTRFS_READ_LOCK_BLOCKING;
1829 }
1830
1831 src_path->slots[cur_level] = dst_path->slots[cur_level];
1832 if (cur_level) {
1833 btrfs_node_key_to_cpu(dst_path->nodes[cur_level],
1834 &dst_key, dst_path->slots[cur_level]);
1835 btrfs_node_key_to_cpu(src_path->nodes[cur_level],
1836 &src_key, src_path->slots[cur_level]);
1837 } else {
1838 btrfs_item_key_to_cpu(dst_path->nodes[cur_level],
1839 &dst_key, dst_path->slots[cur_level]);
1840 btrfs_item_key_to_cpu(src_path->nodes[cur_level],
1841 &src_key, src_path->slots[cur_level]);
1842 }
1843 /* Content mismatch, something went wrong */
1844 if (btrfs_comp_cpu_keys(&dst_key, &src_key)) {
1845 ret = -ENOENT;
1846 goto out;
1847 }
1848 cur_level--;
1849 }
1850
1851 /*
1852 * Now both @dst_path and @src_path have been populated, record the tree
1853 * blocks for qgroup accounting.
1854 */
1855 ret = btrfs_qgroup_trace_extent(trans, src_path->nodes[dst_level]->start,
1856 nodesize, GFP_NOFS);
1857 if (ret < 0)
1858 goto out;
1859 ret = btrfs_qgroup_trace_extent(trans,
1860 dst_path->nodes[dst_level]->start,
1861 nodesize, GFP_NOFS);
1862 if (ret < 0)
1863 goto out;
1864
1865 /* Record leaf file extents */
1866 if (dst_level == 0) {
1867 ret = btrfs_qgroup_trace_leaf_items(trans, src_path->nodes[0]);
1868 if (ret < 0)
1869 goto out;
1870 ret = btrfs_qgroup_trace_leaf_items(trans, dst_path->nodes[0]);
1871 }
1872out:
1873 btrfs_free_path(src_path);
1874 return ret;
1875}
1876
1715int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, 1877int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
1716 struct extent_buffer *root_eb, 1878 struct extent_buffer *root_eb,
1717 u64 root_gen, int root_level) 1879 u64 root_gen, int root_level)