diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-10-15 16:14:48 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:03:56 -0400 |
commit | f510cfecfc98759d75283823cfccf0cc0d59a4c6 (patch) | |
tree | dfc2f788df4e2a437d976f78a2a2a675375f7134 /fs/btrfs/extent-tree.c | |
parent | ae5252bd51a252b7b8b02289337c36774835101c (diff) |
Btrfs: Fix extent_buffer and extent_state leaks
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/extent-tree.c')
-rw-r--r-- | fs/btrfs/extent-tree.c | 126 |
1 files changed, 58 insertions, 68 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 089c41cbca74..74cfbee2ff33 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -34,21 +34,19 @@ static int cache_block_group(struct btrfs_root *root, | |||
34 | int ret; | 34 | int ret; |
35 | struct btrfs_key key; | 35 | struct btrfs_key key; |
36 | struct extent_buffer *leaf; | 36 | struct extent_buffer *leaf; |
37 | struct radix_tree_root *extent_radix; | 37 | struct extent_map_tree *free_space_cache; |
38 | int slot; | 38 | int slot; |
39 | u64 i; | ||
40 | u64 last = 0; | 39 | u64 last = 0; |
41 | u64 hole_size; | 40 | u64 hole_size; |
42 | u64 first_free; | 41 | u64 first_free; |
43 | int found = 0; | 42 | int found = 0; |
44 | 43 | ||
45 | root = root->fs_info->extent_root; | 44 | root = root->fs_info->extent_root; |
46 | extent_radix = &root->fs_info->extent_map_radix; | 45 | free_space_cache = &root->fs_info->free_space_cache; |
47 | 46 | ||
48 | if (block_group->cached) | 47 | if (block_group->cached) |
49 | return 0; | 48 | return 0; |
50 | if (block_group->data) | 49 | |
51 | return 0; | ||
52 | path = btrfs_alloc_path(); | 50 | path = btrfs_alloc_path(); |
53 | if (!path) | 51 | if (!path) |
54 | return -ENOMEM; | 52 | return -ENOMEM; |
@@ -98,9 +96,11 @@ static int cache_block_group(struct btrfs_root *root, | |||
98 | last = first_free; | 96 | last = first_free; |
99 | found = 1; | 97 | found = 1; |
100 | } | 98 | } |
101 | hole_size = key.objectid - last; | 99 | if (key.objectid > last) { |
102 | for (i = 0; i < hole_size; i++) { | 100 | hole_size = key.objectid - last; |
103 | set_radix_bit(extent_radix, last + i); | 101 | set_extent_dirty(free_space_cache, last, |
102 | last + hole_size - 1, | ||
103 | GFP_NOFS); | ||
104 | } | 104 | } |
105 | last = key.objectid + key.offset; | 105 | last = key.objectid + key.offset; |
106 | } | 106 | } |
@@ -114,9 +114,8 @@ next: | |||
114 | block_group->key.offset > last) { | 114 | block_group->key.offset > last) { |
115 | hole_size = block_group->key.objectid + | 115 | hole_size = block_group->key.objectid + |
116 | block_group->key.offset - last; | 116 | block_group->key.offset - last; |
117 | for (i = 0; i < hole_size; i++) { | 117 | set_extent_dirty(free_space_cache, last, |
118 | set_radix_bit(extent_radix, last + i); | 118 | last + hole_size - 1, GFP_NOFS); |
119 | } | ||
120 | } | 119 | } |
121 | block_group->cached = 1; | 120 | block_group->cached = 1; |
122 | err: | 121 | err: |
@@ -150,47 +149,33 @@ struct btrfs_block_group_cache *btrfs_lookup_block_group(struct | |||
150 | return NULL; | 149 | return NULL; |
151 | } | 150 | } |
152 | 151 | ||
153 | static u64 leaf_range(struct btrfs_root *root) | ||
154 | { | ||
155 | u64 size = BTRFS_LEAF_DATA_SIZE(root); | ||
156 | do_div(size, sizeof(struct btrfs_extent_item) + | ||
157 | sizeof(struct btrfs_item)); | ||
158 | return size; | ||
159 | } | ||
160 | |||
161 | static u64 find_search_start(struct btrfs_root *root, | 152 | static u64 find_search_start(struct btrfs_root *root, |
162 | struct btrfs_block_group_cache **cache_ret, | 153 | struct btrfs_block_group_cache **cache_ret, |
163 | u64 search_start, int num) | 154 | u64 search_start, int num, int data) |
164 | { | 155 | { |
165 | unsigned long gang[8]; | ||
166 | int ret; | 156 | int ret; |
167 | struct btrfs_block_group_cache *cache = *cache_ret; | 157 | struct btrfs_block_group_cache *cache = *cache_ret; |
168 | u64 last = max(search_start, cache->key.objectid); | 158 | u64 last = max(search_start, cache->key.objectid); |
159 | u64 start = 0; | ||
160 | u64 end = 0; | ||
169 | 161 | ||
170 | if (cache->data) | ||
171 | goto out; | ||
172 | again: | 162 | again: |
173 | ret = cache_block_group(root, cache); | 163 | ret = cache_block_group(root, cache); |
174 | if (ret) | 164 | if (ret) |
175 | goto out; | 165 | goto out; |
176 | while(1) { | 166 | while(1) { |
177 | ret = find_first_radix_bit(&root->fs_info->extent_map_radix, | 167 | ret = find_first_extent_bit(&root->fs_info->free_space_cache, |
178 | gang, last, ARRAY_SIZE(gang)); | 168 | last, &start, &end, EXTENT_DIRTY); |
179 | if (!ret) | 169 | if (ret) |
180 | goto out; | 170 | goto out; |
181 | last = gang[ret-1] + 1; | 171 | |
182 | if (num > 1) { | 172 | start = max(last, start); |
183 | if (ret != ARRAY_SIZE(gang)) { | 173 | last = end + 1; |
184 | goto new_group; | 174 | if (end + 1 - start < num) |
185 | } | 175 | continue; |
186 | if (gang[ret-1] - gang[0] > leaf_range(root)) { | 176 | if (start + num > cache->key.objectid + cache->key.offset) |
187 | continue; | ||
188 | } | ||
189 | } | ||
190 | if (gang[0] >= cache->key.objectid + cache->key.offset) { | ||
191 | goto new_group; | 177 | goto new_group; |
192 | } | 178 | return start; |
193 | return gang[0]; | ||
194 | } | 179 | } |
195 | out: | 180 | out: |
196 | return max(cache->last_alloc, search_start); | 181 | return max(cache->last_alloc, search_start); |
@@ -202,7 +187,7 @@ new_group: | |||
202 | return max((*cache_ret)->last_alloc, search_start); | 187 | return max((*cache_ret)->last_alloc, search_start); |
203 | } | 188 | } |
204 | cache = btrfs_find_block_group(root, cache, | 189 | cache = btrfs_find_block_group(root, cache, |
205 | last + cache->key.offset - 1, 0, 0); | 190 | last + cache->key.offset - 1, data, 0); |
206 | *cache_ret = cache; | 191 | *cache_ret = cache; |
207 | goto again; | 192 | goto again; |
208 | } | 193 | } |
@@ -625,7 +610,6 @@ static int update_block_group(struct btrfs_trans_handle *trans, | |||
625 | u64 total = num; | 610 | u64 total = num; |
626 | u64 old_val; | 611 | u64 old_val; |
627 | u64 block_in_group; | 612 | u64 block_in_group; |
628 | u64 i; | ||
629 | int ret; | 613 | int ret; |
630 | 614 | ||
631 | while(total) { | 615 | while(total) { |
@@ -644,12 +628,6 @@ static int update_block_group(struct btrfs_trans_handle *trans, | |||
644 | if (alloc) { | 628 | if (alloc) { |
645 | if (blocknr > cache->last_alloc) | 629 | if (blocknr > cache->last_alloc) |
646 | cache->last_alloc = blocknr; | 630 | cache->last_alloc = blocknr; |
647 | if (!cache->data) { | ||
648 | for (i = 0; i < num; i++) { | ||
649 | clear_radix_bit(&info->extent_map_radix, | ||
650 | blocknr + i); | ||
651 | } | ||
652 | } | ||
653 | if (cache->data != data && | 631 | if (cache->data != data && |
654 | old_val < (cache->key.offset >> 1)) { | 632 | old_val < (cache->key.offset >> 1)) { |
655 | cache->data = data; | 633 | cache->data = data; |
@@ -677,11 +655,10 @@ static int update_block_group(struct btrfs_trans_handle *trans, | |||
677 | old_val -= num; | 655 | old_val -= num; |
678 | if (blocknr < cache->first_free) | 656 | if (blocknr < cache->first_free) |
679 | cache->first_free = blocknr; | 657 | cache->first_free = blocknr; |
680 | if (!cache->data && mark_free) { | 658 | if (mark_free) { |
681 | for (i = 0; i < num; i++) { | 659 | set_extent_dirty(&info->free_space_cache, |
682 | set_radix_bit(&info->extent_map_radix, | 660 | blocknr, blocknr + num - 1, |
683 | blocknr + i); | 661 | GFP_NOFS); |
684 | } | ||
685 | } | 662 | } |
686 | if (old_val < (cache->key.offset >> 1) && | 663 | if (old_val < (cache->key.offset >> 1) && |
687 | old_val + num >= (cache->key.offset >> 1)) { | 664 | old_val + num >= (cache->key.offset >> 1)) { |
@@ -732,7 +709,9 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, | |||
732 | int ret; | 709 | int ret; |
733 | int i; | 710 | int i; |
734 | struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix; | 711 | struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix; |
735 | struct radix_tree_root *extent_radix = &root->fs_info->extent_map_radix; | 712 | struct extent_map_tree *free_space_cache; |
713 | |||
714 | free_space_cache = &root->fs_info->free_space_cache; | ||
736 | 715 | ||
737 | while(1) { | 716 | while(1) { |
738 | ret = find_first_radix_bit(unpin_radix, gang, 0, | 717 | ret = find_first_radix_bit(unpin_radix, gang, 0, |
@@ -751,8 +730,11 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, | |||
751 | block_group->pinned--; | 730 | block_group->pinned--; |
752 | if (gang[i] < block_group->last_alloc) | 731 | if (gang[i] < block_group->last_alloc) |
753 | block_group->last_alloc = gang[i]; | 732 | block_group->last_alloc = gang[i]; |
754 | if (!block_group->data) | 733 | if (!block_group->data) { |
755 | set_radix_bit(extent_radix, gang[i]); | 734 | set_extent_dirty(free_space_cache, |
735 | gang[i], gang[i], | ||
736 | GFP_NOFS); | ||
737 | } | ||
756 | } | 738 | } |
757 | } | 739 | } |
758 | } | 740 | } |
@@ -995,6 +977,7 @@ static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root | |||
995 | struct btrfs_block_group_cache *block_group; | 977 | struct btrfs_block_group_cache *block_group; |
996 | int full_scan = 0; | 978 | int full_scan = 0; |
997 | int wrapped = 0; | 979 | int wrapped = 0; |
980 | u64 cached_search_start = 0; | ||
998 | 981 | ||
999 | WARN_ON(num_blocks < 1); | 982 | WARN_ON(num_blocks < 1); |
1000 | btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY); | 983 | btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY); |
@@ -1017,11 +1000,9 @@ static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1017 | path = btrfs_alloc_path(); | 1000 | path = btrfs_alloc_path(); |
1018 | 1001 | ||
1019 | check_failed: | 1002 | check_failed: |
1020 | if (!block_group->data) | 1003 | search_start = find_search_start(root, &block_group, |
1021 | search_start = find_search_start(root, &block_group, | 1004 | search_start, total_needed, data); |
1022 | search_start, total_needed); | 1005 | cached_search_start = search_start; |
1023 | else if (!full_scan) | ||
1024 | search_start = max(block_group->last_alloc, search_start); | ||
1025 | 1006 | ||
1026 | btrfs_init_path(path); | 1007 | btrfs_init_path(path); |
1027 | ins->objectid = search_start; | 1008 | ins->objectid = search_start; |
@@ -1097,6 +1078,7 @@ check_failed: | |||
1097 | 1078 | ||
1098 | start_found = 1; | 1079 | start_found = 1; |
1099 | last_block = key.objectid + key.offset; | 1080 | last_block = key.objectid + key.offset; |
1081 | |||
1100 | if (!full_scan && last_block >= block_group->key.objectid + | 1082 | if (!full_scan && last_block >= block_group->key.objectid + |
1101 | block_group->key.offset) { | 1083 | block_group->key.offset) { |
1102 | btrfs_release_path(root, path); | 1084 | btrfs_release_path(root, path); |
@@ -1138,6 +1120,9 @@ check_pending: | |||
1138 | } | 1120 | } |
1139 | ins->offset = num_blocks; | 1121 | ins->offset = num_blocks; |
1140 | btrfs_free_path(path); | 1122 | btrfs_free_path(path); |
1123 | if (0 && ins->objectid != cached_search_start) { | ||
1124 | printk("\tcached was %Lu found %Lu\n", cached_search_start, ins->objectid); | ||
1125 | } | ||
1141 | return 0; | 1126 | return 0; |
1142 | 1127 | ||
1143 | new_group: | 1128 | new_group: |
@@ -1209,6 +1194,10 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, | |||
1209 | btrfs_set_root_used(&root->root_item, root_blocks_used + | 1194 | btrfs_set_root_used(&root->root_item, root_blocks_used + |
1210 | num_blocks); | 1195 | num_blocks); |
1211 | 1196 | ||
1197 | clear_extent_dirty(&root->fs_info->free_space_cache, | ||
1198 | ins->objectid, ins->objectid + ins->offset - 1, | ||
1199 | GFP_NOFS); | ||
1200 | |||
1212 | if (root == extent_root) { | 1201 | if (root == extent_root) { |
1213 | BUG_ON(num_blocks != 1); | 1202 | BUG_ON(num_blocks != 1); |
1214 | set_radix_bit(&root->fs_info->extent_ins_radix, ins->objectid); | 1203 | set_radix_bit(&root->fs_info->extent_ins_radix, ins->objectid); |
@@ -1227,6 +1216,7 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, | |||
1227 | BUG_ON(ret); | 1216 | BUG_ON(ret); |
1228 | finish_current_insert(trans, extent_root); | 1217 | finish_current_insert(trans, extent_root); |
1229 | pending_ret = del_pending_extents(trans, extent_root); | 1218 | pending_ret = del_pending_extents(trans, extent_root); |
1219 | |||
1230 | if (ret) { | 1220 | if (ret) { |
1231 | return ret; | 1221 | return ret; |
1232 | } | 1222 | } |
@@ -1265,6 +1255,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, | |||
1265 | return ERR_PTR(-ENOMEM); | 1255 | return ERR_PTR(-ENOMEM); |
1266 | } | 1256 | } |
1267 | btrfs_set_buffer_uptodate(buf); | 1257 | btrfs_set_buffer_uptodate(buf); |
1258 | buf->alloc_addr = (unsigned long)__builtin_return_address(0); | ||
1268 | set_extent_dirty(&trans->transaction->dirty_pages, buf->start, | 1259 | set_extent_dirty(&trans->transaction->dirty_pages, buf->start, |
1269 | buf->start + buf->len - 1, GFP_NOFS); | 1260 | buf->start + buf->len - 1, GFP_NOFS); |
1270 | /* | 1261 | /* |
@@ -1492,6 +1483,7 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1492 | orig_level = level; | 1483 | orig_level = level; |
1493 | if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { | 1484 | if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) { |
1494 | path->nodes[level] = root->node; | 1485 | path->nodes[level] = root->node; |
1486 | extent_buffer_get(root->node); | ||
1495 | path->slots[level] = 0; | 1487 | path->slots[level] = 0; |
1496 | } else { | 1488 | } else { |
1497 | struct btrfs_key key; | 1489 | struct btrfs_key key; |
@@ -1524,7 +1516,6 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
1524 | if (wret < 0) | 1516 | if (wret < 0) |
1525 | ret = wret; | 1517 | ret = wret; |
1526 | ret = -EAGAIN; | 1518 | ret = -EAGAIN; |
1527 | extent_buffer_get(root->node); | ||
1528 | break; | 1519 | break; |
1529 | } | 1520 | } |
1530 | for (i = 0; i <= orig_level; i++) { | 1521 | for (i = 0; i <= orig_level; i++) { |
@@ -1562,8 +1553,8 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info) | |||
1562 | { | 1553 | { |
1563 | int ret; | 1554 | int ret; |
1564 | int ret2; | 1555 | int ret2; |
1565 | unsigned long gang[16]; | 1556 | u64 start; |
1566 | int i; | 1557 | u64 end; |
1567 | 1558 | ||
1568 | ret = free_block_group_radix(&info->block_group_radix); | 1559 | ret = free_block_group_radix(&info->block_group_radix); |
1569 | ret2 = free_block_group_radix(&info->block_group_data_radix); | 1560 | ret2 = free_block_group_radix(&info->block_group_data_radix); |
@@ -1573,13 +1564,12 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info) | |||
1573 | return ret2; | 1564 | return ret2; |
1574 | 1565 | ||
1575 | while(1) { | 1566 | while(1) { |
1576 | ret = find_first_radix_bit(&info->extent_map_radix, | 1567 | ret = find_first_extent_bit(&info->free_space_cache, 0, |
1577 | gang, 0, ARRAY_SIZE(gang)); | 1568 | &start, &end, EXTENT_DIRTY); |
1578 | if (!ret) | 1569 | if (ret) |
1579 | break; | 1570 | break; |
1580 | for (i = 0; i < ret; i++) { | 1571 | clear_extent_dirty(&info->free_space_cache, start, |
1581 | clear_radix_bit(&info->extent_map_radix, gang[i]); | 1572 | end, GFP_NOFS); |
1582 | } | ||
1583 | } | 1573 | } |
1584 | return 0; | 1574 | return 0; |
1585 | } | 1575 | } |