diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-10-25 15:42:57 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:03:57 -0400 |
commit | cc0c55384796b422133ff1f21646835b31590f88 (patch) | |
tree | 152933613a431cce89a5cdcd0c5761688f8e5124 /fs/btrfs/ctree.c | |
parent | 65555a06b4d1ae116ce223dc4b82d6068b36df96 (diff) |
Btrfs: Fix split_leaf to detect when it is extending an item
When making room for a new item, it is ok to create an empty leaf, but
when making room to extend an item, split_leaf needs to make sure it
keeps the item we're extending in the path and make sure we don't end up
with an empty leaf.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r-- | fs/btrfs/ctree.c | 78 |
1 files changed, 27 insertions, 51 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 53e40b5c084d..3eb5a9f30d14 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -26,7 +26,7 @@ static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root | |||
26 | *root, struct btrfs_path *path, int level); | 26 | *root, struct btrfs_path *path, int level); |
27 | static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root | 27 | static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root |
28 | *root, struct btrfs_key *ins_key, | 28 | *root, struct btrfs_key *ins_key, |
29 | struct btrfs_path *path, int data_size); | 29 | struct btrfs_path *path, int data_size, int extend); |
30 | static int push_node_left(struct btrfs_trans_handle *trans, | 30 | static int push_node_left(struct btrfs_trans_handle *trans, |
31 | struct btrfs_root *root, struct extent_buffer *dst, | 31 | struct btrfs_root *root, struct extent_buffer *dst, |
32 | struct extent_buffer *src); | 32 | struct extent_buffer *src); |
@@ -1049,7 +1049,7 @@ again: | |||
1049 | if (ins_len > 0 && btrfs_leaf_free_space(root, b) < | 1049 | if (ins_len > 0 && btrfs_leaf_free_space(root, b) < |
1050 | sizeof(struct btrfs_item) + ins_len) { | 1050 | sizeof(struct btrfs_item) + ins_len) { |
1051 | int sret = split_leaf(trans, root, key, | 1051 | int sret = split_leaf(trans, root, key, |
1052 | p, ins_len); | 1052 | p, ins_len, ret == 0); |
1053 | BUG_ON(sret > 0); | 1053 | BUG_ON(sret > 0); |
1054 | if (sret) | 1054 | if (sret) |
1055 | return sret; | 1055 | return sret; |
@@ -1755,7 +1755,7 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1755 | */ | 1755 | */ |
1756 | static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root | 1756 | static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root |
1757 | *root, struct btrfs_key *ins_key, | 1757 | *root, struct btrfs_key *ins_key, |
1758 | struct btrfs_path *path, int data_size) | 1758 | struct btrfs_path *path, int data_size, int extend) |
1759 | { | 1759 | { |
1760 | struct extent_buffer *l; | 1760 | struct extent_buffer *l; |
1761 | u32 nritems; | 1761 | u32 nritems; |
@@ -1768,9 +1768,13 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1768 | int i; | 1768 | int i; |
1769 | int ret = 0; | 1769 | int ret = 0; |
1770 | int wret; | 1770 | int wret; |
1771 | int double_split = 0; | 1771 | int double_split; |
1772 | int num_doubles = 0; | ||
1772 | struct btrfs_disk_key disk_key; | 1773 | struct btrfs_disk_key disk_key; |
1773 | 1774 | ||
1775 | if (extend) | ||
1776 | space_needed = data_size; | ||
1777 | |||
1774 | /* first try to make some room by pushing left and right */ | 1778 | /* first try to make some room by pushing left and right */ |
1775 | if (ins_key->type != BTRFS_DIR_ITEM_KEY) { | 1779 | if (ins_key->type != BTRFS_DIR_ITEM_KEY) { |
1776 | wret = push_leaf_right(trans, root, path, data_size); | 1780 | wret = push_leaf_right(trans, root, path, data_size); |
@@ -1785,12 +1789,8 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1785 | l = path->nodes[0]; | 1789 | l = path->nodes[0]; |
1786 | 1790 | ||
1787 | /* did the pushes work? */ | 1791 | /* did the pushes work? */ |
1788 | if (btrfs_leaf_free_space(root, l) >= | 1792 | if (btrfs_leaf_free_space(root, l) >= space_needed) |
1789 | sizeof(struct btrfs_item) + data_size) { | ||
1790 | return 0; | 1793 | return 0; |
1791 | } | ||
1792 | } else { | ||
1793 | l = path->nodes[0]; | ||
1794 | } | 1794 | } |
1795 | 1795 | ||
1796 | if (!path->nodes[1]) { | 1796 | if (!path->nodes[1]) { |
@@ -1798,6 +1798,9 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1798 | if (ret) | 1798 | if (ret) |
1799 | return ret; | 1799 | return ret; |
1800 | } | 1800 | } |
1801 | again: | ||
1802 | double_split = 0; | ||
1803 | l = path->nodes[0]; | ||
1801 | slot = path->slots[0]; | 1804 | slot = path->slots[0]; |
1802 | nritems = btrfs_header_nritems(l); | 1805 | nritems = btrfs_header_nritems(l); |
1803 | mid = (nritems + 1)/ 2; | 1806 | mid = (nritems + 1)/ 2; |
@@ -1815,7 +1818,6 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1815 | write_extent_buffer(right, root->fs_info->fsid, | 1818 | write_extent_buffer(right, root->fs_info->fsid, |
1816 | (unsigned long)btrfs_header_fsid(right), | 1819 | (unsigned long)btrfs_header_fsid(right), |
1817 | BTRFS_FSID_SIZE); | 1820 | BTRFS_FSID_SIZE); |
1818 | |||
1819 | if (mid <= slot) { | 1821 | if (mid <= slot) { |
1820 | if (nritems == 1 || | 1822 | if (nritems == 1 || |
1821 | leaf_space_used(l, mid, nritems - mid) + space_needed > | 1823 | leaf_space_used(l, mid, nritems - mid) + space_needed > |
@@ -1844,7 +1846,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1844 | } else { | 1846 | } else { |
1845 | if (leaf_space_used(l, 0, mid + 1) + space_needed > | 1847 | if (leaf_space_used(l, 0, mid + 1) + space_needed > |
1846 | BTRFS_LEAF_DATA_SIZE(root)) { | 1848 | BTRFS_LEAF_DATA_SIZE(root)) { |
1847 | if (slot == 0) { | 1849 | if (!extend && slot == 0) { |
1848 | btrfs_cpu_key_to_disk(&disk_key, ins_key); | 1850 | btrfs_cpu_key_to_disk(&disk_key, ins_key); |
1849 | btrfs_set_header_nritems(right, 0); | 1851 | btrfs_set_header_nritems(right, 0); |
1850 | wret = insert_ptr(trans, root, path, | 1852 | wret = insert_ptr(trans, root, path, |
@@ -1863,12 +1865,15 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1863 | ret = wret; | 1865 | ret = wret; |
1864 | } | 1866 | } |
1865 | return ret; | 1867 | return ret; |
1866 | } | 1868 | } else if (extend && slot == 0) { |
1867 | mid = slot; | 1869 | mid = 1; |
1868 | if (mid != nritems && | 1870 | } else { |
1869 | leaf_space_used(l, mid, nritems - mid) + | 1871 | mid = slot; |
1870 | space_needed > BTRFS_LEAF_DATA_SIZE(root)) { | 1872 | if (mid != nritems && |
1871 | double_split = 1; | 1873 | leaf_space_used(l, mid, nritems - mid) + |
1874 | space_needed > BTRFS_LEAF_DATA_SIZE(root)) { | ||
1875 | double_split = 1; | ||
1876 | } | ||
1872 | } | 1877 | } |
1873 | } | 1878 | } |
1874 | } | 1879 | } |
@@ -1931,39 +1936,11 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1931 | 1936 | ||
1932 | BUG_ON(path->slots[0] < 0); | 1937 | BUG_ON(path->slots[0] < 0); |
1933 | 1938 | ||
1934 | if (!double_split) { | 1939 | if (double_split) { |
1935 | return ret; | 1940 | BUG_ON(num_doubles != 0); |
1936 | } | 1941 | num_doubles++; |
1937 | 1942 | goto again; | |
1938 | right = btrfs_alloc_free_block(trans, root, root->leafsize, | ||
1939 | l->start, 0); | ||
1940 | if (IS_ERR(right)) | ||
1941 | return PTR_ERR(right); | ||
1942 | |||
1943 | memset_extent_buffer(right, 0, 0, sizeof(struct btrfs_header)); | ||
1944 | btrfs_set_header_bytenr(right, right->start); | ||
1945 | btrfs_set_header_generation(right, trans->transid); | ||
1946 | btrfs_set_header_owner(right, root->root_key.objectid); | ||
1947 | btrfs_set_header_level(right, 0); | ||
1948 | write_extent_buffer(right, root->fs_info->fsid, | ||
1949 | (unsigned long)btrfs_header_fsid(right), | ||
1950 | BTRFS_FSID_SIZE); | ||
1951 | |||
1952 | btrfs_cpu_key_to_disk(&disk_key, ins_key); | ||
1953 | btrfs_set_header_nritems(right, 0); | ||
1954 | wret = insert_ptr(trans, root, path, | ||
1955 | &disk_key, right->start, | ||
1956 | path->slots[1], 1); | ||
1957 | if (wret) | ||
1958 | ret = wret; | ||
1959 | if (path->slots[1] == 0) { | ||
1960 | wret = fixup_low_keys(trans, root, path, &disk_key, 1); | ||
1961 | if (wret) | ||
1962 | ret = wret; | ||
1963 | } | 1943 | } |
1964 | free_extent_buffer(path->nodes[0]); | ||
1965 | path->nodes[0] = right; | ||
1966 | path->slots[0] = 0; | ||
1967 | return ret; | 1944 | return ret; |
1968 | } | 1945 | } |
1969 | 1946 | ||
@@ -1992,8 +1969,7 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans, | |||
1992 | 1969 | ||
1993 | slot = path->slots[0]; | 1970 | slot = path->slots[0]; |
1994 | old_data_start = btrfs_item_offset_nr(leaf, slot); | 1971 | old_data_start = btrfs_item_offset_nr(leaf, slot); |
1995 | old_size = btrfs_item_size_nr(leaf, slot); | 1972 | old_size = btrfs_item_size_nr(leaf, slot); BUG_ON(old_size <= new_size); |
1996 | BUG_ON(old_size <= new_size); | ||
1997 | size_diff = old_size - new_size; | 1973 | size_diff = old_size - new_size; |
1998 | 1974 | ||
1999 | BUG_ON(slot < 0); | 1975 | BUG_ON(slot < 0); |