diff options
author | Josef Bacik <jbacik@fusionio.com> | 2012-06-27 15:10:56 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@fusionio.com> | 2012-07-02 15:39:18 -0400 |
commit | bdb7d303b33c1648514c9f9461d7513a4c05ce48 (patch) | |
tree | 78e24aa16c3d3cec9133616fa9076f7420e03612 /fs | |
parent | 6bf02314d9a5c29f6ec30285b9ad5361c2d4c85a (diff) |
Btrfs: fix tree log remove space corner case
The tree log stuff can have allocated space that we end up having split
across a bitmap and a real extent. The free space code does not deal with
this, it assumes that if it finds an extent or bitmap entry that the entire
range must fall within the entry it finds. This isn't necessarily the case,
so rework the remove function so it can handle this case properly. This
fixed two panics the user hit, first in the case where the space was
initially in a bitmap and then in an extent entry, and then the reverse
case. Thanks,
Reported-and-tested-by: Shaun Reich <sreich@kde.org>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/free-space-cache.c | 145 |
1 files changed, 52 insertions, 93 deletions
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index 19a0d85b451c..a70c54e2e1be 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c | |||
@@ -1542,29 +1542,26 @@ again: | |||
1542 | end = bitmap_info->offset + (u64)(BITS_PER_BITMAP * ctl->unit) - 1; | 1542 | end = bitmap_info->offset + (u64)(BITS_PER_BITMAP * ctl->unit) - 1; |
1543 | 1543 | ||
1544 | /* | 1544 | /* |
1545 | * XXX - this can go away after a few releases. | 1545 | * We need to search for bits in this bitmap. We could only cover some |
1546 | * | 1546 | * of the extent in this bitmap thanks to how we add space, so we need |
1547 | * since the only user of btrfs_remove_free_space is the tree logging | 1547 | * to search for as much as it as we can and clear that amount, and then |
1548 | * stuff, and the only way to test that is under crash conditions, we | 1548 | * go searching for the next bit. |
1549 | * want to have this debug stuff here just in case somethings not | ||
1550 | * working. Search the bitmap for the space we are trying to use to | ||
1551 | * make sure its actually there. If its not there then we need to stop | ||
1552 | * because something has gone wrong. | ||
1553 | */ | 1549 | */ |
1554 | search_start = *offset; | 1550 | search_start = *offset; |
1555 | search_bytes = *bytes; | 1551 | search_bytes = ctl->unit; |
1556 | search_bytes = min(search_bytes, end - search_start + 1); | 1552 | search_bytes = min(search_bytes, end - search_start + 1); |
1557 | ret = search_bitmap(ctl, bitmap_info, &search_start, &search_bytes); | 1553 | ret = search_bitmap(ctl, bitmap_info, &search_start, &search_bytes); |
1558 | BUG_ON(ret < 0 || search_start != *offset); | 1554 | BUG_ON(ret < 0 || search_start != *offset); |
1559 | 1555 | ||
1560 | if (*offset > bitmap_info->offset && *offset + *bytes > end) { | 1556 | /* We may have found more bits than what we need */ |
1561 | bitmap_clear_bits(ctl, bitmap_info, *offset, end - *offset + 1); | 1557 | search_bytes = min(search_bytes, *bytes); |
1562 | *bytes -= end - *offset + 1; | 1558 | |
1563 | *offset = end + 1; | 1559 | /* Cannot clear past the end of the bitmap */ |
1564 | } else if (*offset >= bitmap_info->offset && *offset + *bytes <= end) { | 1560 | search_bytes = min(search_bytes, end - search_start + 1); |
1565 | bitmap_clear_bits(ctl, bitmap_info, *offset, *bytes); | 1561 | |
1566 | *bytes = 0; | 1562 | bitmap_clear_bits(ctl, bitmap_info, search_start, search_bytes); |
1567 | } | 1563 | *offset += search_bytes; |
1564 | *bytes -= search_bytes; | ||
1568 | 1565 | ||
1569 | if (*bytes) { | 1566 | if (*bytes) { |
1570 | struct rb_node *next = rb_next(&bitmap_info->offset_index); | 1567 | struct rb_node *next = rb_next(&bitmap_info->offset_index); |
@@ -1595,7 +1592,7 @@ again: | |||
1595 | * everything over again. | 1592 | * everything over again. |
1596 | */ | 1593 | */ |
1597 | search_start = *offset; | 1594 | search_start = *offset; |
1598 | search_bytes = *bytes; | 1595 | search_bytes = ctl->unit; |
1599 | ret = search_bitmap(ctl, bitmap_info, &search_start, | 1596 | ret = search_bitmap(ctl, bitmap_info, &search_start, |
1600 | &search_bytes); | 1597 | &search_bytes); |
1601 | if (ret < 0 || search_start != *offset) | 1598 | if (ret < 0 || search_start != *offset) |
@@ -1878,12 +1875,14 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group, | |||
1878 | { | 1875 | { |
1879 | struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; | 1876 | struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; |
1880 | struct btrfs_free_space *info; | 1877 | struct btrfs_free_space *info; |
1881 | struct btrfs_free_space *next_info = NULL; | ||
1882 | int ret = 0; | 1878 | int ret = 0; |
1883 | 1879 | ||
1884 | spin_lock(&ctl->tree_lock); | 1880 | spin_lock(&ctl->tree_lock); |
1885 | 1881 | ||
1886 | again: | 1882 | again: |
1883 | if (!bytes) | ||
1884 | goto out_lock; | ||
1885 | |||
1887 | info = tree_search_offset(ctl, offset, 0, 0); | 1886 | info = tree_search_offset(ctl, offset, 0, 0); |
1888 | if (!info) { | 1887 | if (!info) { |
1889 | /* | 1888 | /* |
@@ -1904,88 +1903,48 @@ again: | |||
1904 | } | 1903 | } |
1905 | } | 1904 | } |
1906 | 1905 | ||
1907 | if (info->bytes < bytes && rb_next(&info->offset_index)) { | 1906 | if (!info->bitmap) { |
1908 | u64 end; | ||
1909 | next_info = rb_entry(rb_next(&info->offset_index), | ||
1910 | struct btrfs_free_space, | ||
1911 | offset_index); | ||
1912 | |||
1913 | if (next_info->bitmap) | ||
1914 | end = next_info->offset + | ||
1915 | BITS_PER_BITMAP * ctl->unit - 1; | ||
1916 | else | ||
1917 | end = next_info->offset + next_info->bytes; | ||
1918 | |||
1919 | if (next_info->bytes < bytes || | ||
1920 | next_info->offset > offset || offset > end) { | ||
1921 | printk(KERN_CRIT "Found free space at %llu, size %llu," | ||
1922 | " trying to use %llu\n", | ||
1923 | (unsigned long long)info->offset, | ||
1924 | (unsigned long long)info->bytes, | ||
1925 | (unsigned long long)bytes); | ||
1926 | WARN_ON(1); | ||
1927 | ret = -EINVAL; | ||
1928 | goto out_lock; | ||
1929 | } | ||
1930 | |||
1931 | info = next_info; | ||
1932 | } | ||
1933 | |||
1934 | if (info->bytes == bytes) { | ||
1935 | unlink_free_space(ctl, info); | 1907 | unlink_free_space(ctl, info); |
1936 | if (info->bitmap) { | 1908 | if (offset == info->offset) { |
1937 | kfree(info->bitmap); | 1909 | u64 to_free = min(bytes, info->bytes); |
1938 | ctl->total_bitmaps--; | 1910 | |
1939 | } | 1911 | info->bytes -= to_free; |
1940 | kmem_cache_free(btrfs_free_space_cachep, info); | 1912 | info->offset += to_free; |
1941 | ret = 0; | 1913 | if (info->bytes) { |
1942 | goto out_lock; | 1914 | ret = link_free_space(ctl, info); |
1943 | } | 1915 | WARN_ON(ret); |
1944 | 1916 | } else { | |
1945 | if (!info->bitmap && info->offset == offset) { | 1917 | kmem_cache_free(btrfs_free_space_cachep, info); |
1946 | unlink_free_space(ctl, info); | 1918 | } |
1947 | info->offset += bytes; | ||
1948 | info->bytes -= bytes; | ||
1949 | ret = link_free_space(ctl, info); | ||
1950 | WARN_ON(ret); | ||
1951 | goto out_lock; | ||
1952 | } | ||
1953 | 1919 | ||
1954 | if (!info->bitmap && info->offset <= offset && | 1920 | offset += to_free; |
1955 | info->offset + info->bytes >= offset + bytes) { | 1921 | bytes -= to_free; |
1956 | u64 old_start = info->offset; | 1922 | goto again; |
1957 | /* | 1923 | } else { |
1958 | * we're freeing space in the middle of the info, | 1924 | u64 old_end = info->bytes + info->offset; |
1959 | * this can happen during tree log replay | ||
1960 | * | ||
1961 | * first unlink the old info and then | ||
1962 | * insert it again after the hole we're creating | ||
1963 | */ | ||
1964 | unlink_free_space(ctl, info); | ||
1965 | if (offset + bytes < info->offset + info->bytes) { | ||
1966 | u64 old_end = info->offset + info->bytes; | ||
1967 | 1925 | ||
1968 | info->offset = offset + bytes; | 1926 | info->bytes = offset - info->offset; |
1969 | info->bytes = old_end - info->offset; | ||
1970 | ret = link_free_space(ctl, info); | 1927 | ret = link_free_space(ctl, info); |
1971 | WARN_ON(ret); | 1928 | WARN_ON(ret); |
1972 | if (ret) | 1929 | if (ret) |
1973 | goto out_lock; | 1930 | goto out_lock; |
1974 | } else { | ||
1975 | /* the hole we're creating ends at the end | ||
1976 | * of the info struct, just free the info | ||
1977 | */ | ||
1978 | kmem_cache_free(btrfs_free_space_cachep, info); | ||
1979 | } | ||
1980 | spin_unlock(&ctl->tree_lock); | ||
1981 | 1931 | ||
1982 | /* step two, insert a new info struct to cover | 1932 | /* Not enough bytes in this entry to satisfy us */ |
1983 | * anything before the hole | 1933 | if (old_end < offset + bytes) { |
1984 | */ | 1934 | bytes -= old_end - offset; |
1985 | ret = btrfs_add_free_space(block_group, old_start, | 1935 | offset = old_end; |
1986 | offset - old_start); | 1936 | goto again; |
1987 | WARN_ON(ret); /* -ENOMEM */ | 1937 | } else if (old_end == offset + bytes) { |
1988 | goto out; | 1938 | /* all done */ |
1939 | goto out_lock; | ||
1940 | } | ||
1941 | spin_unlock(&ctl->tree_lock); | ||
1942 | |||
1943 | ret = btrfs_add_free_space(block_group, offset + bytes, | ||
1944 | old_end - (offset + bytes)); | ||
1945 | WARN_ON(ret); | ||
1946 | goto out; | ||
1947 | } | ||
1989 | } | 1948 | } |
1990 | 1949 | ||
1991 | ret = remove_from_bitmap(ctl, info, &offset, &bytes); | 1950 | ret = remove_from_bitmap(ctl, info, &offset, &bytes); |