diff options
Diffstat (limited to 'fs/btrfs/extent-tree.c')
-rw-r--r-- | fs/btrfs/extent-tree.c | 204 |
1 files changed, 188 insertions, 16 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index e6fe3fd38819..0bb4fc83cfd6 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -127,6 +127,105 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
127 | return 0; | 127 | return 0; |
128 | } | 128 | } |
129 | 129 | ||
130 | static int write_one_cache_group(struct btrfs_trans_handle *trans, | ||
131 | struct btrfs_root *root, | ||
132 | struct btrfs_path *path, | ||
133 | struct btrfs_block_group_cache *cache) | ||
134 | { | ||
135 | int ret; | ||
136 | int pending_ret; | ||
137 | struct btrfs_root *extent_root = root->fs_info->extent_root; | ||
138 | struct btrfs_block_group_item *bi; | ||
139 | struct btrfs_key ins; | ||
140 | |||
141 | find_free_extent(trans, extent_root, 0, 0, (u64)-1, &ins); | ||
142 | ret = btrfs_search_slot(trans, extent_root, &cache->key, path, 0, 1); | ||
143 | BUG_ON(ret); | ||
144 | bi = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0], | ||
145 | struct btrfs_block_group_item); | ||
146 | memcpy(bi, &cache->item, sizeof(*bi)); | ||
147 | mark_buffer_dirty(path->nodes[0]); | ||
148 | btrfs_release_path(extent_root, path); | ||
149 | |||
150 | finish_current_insert(trans, extent_root); | ||
151 | pending_ret = del_pending_extents(trans, extent_root); | ||
152 | if (ret) | ||
153 | return ret; | ||
154 | if (pending_ret) | ||
155 | return pending_ret; | ||
156 | return 0; | ||
157 | |||
158 | } | ||
159 | |||
160 | int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans, | ||
161 | struct btrfs_root *root) | ||
162 | { | ||
163 | struct btrfs_block_group_cache *cache[8]; | ||
164 | int ret; | ||
165 | int err = 0; | ||
166 | int werr = 0; | ||
167 | struct radix_tree_root *radix = &root->fs_info->block_group_radix; | ||
168 | int i; | ||
169 | struct btrfs_path *path; | ||
170 | |||
171 | path = btrfs_alloc_path(); | ||
172 | if (!path) | ||
173 | return -ENOMEM; | ||
174 | |||
175 | while(1) { | ||
176 | ret = radix_tree_gang_lookup_tag(radix, (void **)cache, | ||
177 | 0, ARRAY_SIZE(cache), | ||
178 | BTRFS_BLOCK_GROUP_DIRTY); | ||
179 | if (!ret) | ||
180 | break; | ||
181 | for (i = 0; i < ret; i++) { | ||
182 | radix_tree_tag_clear(radix, cache[i]->key.objectid + | ||
183 | cache[i]->key.offset - 1, | ||
184 | BTRFS_BLOCK_GROUP_DIRTY); | ||
185 | err = write_one_cache_group(trans, root, | ||
186 | path, cache[i]); | ||
187 | if (err) | ||
188 | werr = err; | ||
189 | } | ||
190 | } | ||
191 | btrfs_free_path(path); | ||
192 | return werr; | ||
193 | } | ||
194 | |||
195 | static int update_block_group(struct btrfs_trans_handle *trans, | ||
196 | struct btrfs_root *root, | ||
197 | u64 blocknr, u64 num, int alloc) | ||
198 | { | ||
199 | struct btrfs_block_group_cache *cache; | ||
200 | struct btrfs_fs_info *info = root->fs_info; | ||
201 | u64 total = num; | ||
202 | u64 old_val; | ||
203 | u64 block_in_group; | ||
204 | int ret; | ||
205 | while(total) { | ||
206 | ret = radix_tree_gang_lookup(&info->block_group_radix, | ||
207 | (void **)&cache, blocknr, 1); | ||
208 | if (!ret) | ||
209 | return -1; | ||
210 | block_in_group = blocknr - cache->key.objectid; | ||
211 | WARN_ON(block_in_group > cache->key.offset); | ||
212 | radix_tree_tag_set(&info->block_group_radix, | ||
213 | cache->key.objectid + cache->key.offset - 1, | ||
214 | BTRFS_BLOCK_GROUP_DIRTY); | ||
215 | |||
216 | old_val = btrfs_block_group_used(&cache->item); | ||
217 | num = min(total, cache->key.offset - block_in_group); | ||
218 | total -= num; | ||
219 | blocknr += num; | ||
220 | if (alloc) | ||
221 | old_val += num; | ||
222 | else | ||
223 | old_val -= num; | ||
224 | btrfs_set_block_group_used(&cache->item, old_val); | ||
225 | } | ||
226 | return 0; | ||
227 | } | ||
228 | |||
130 | int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct | 229 | int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct |
131 | btrfs_root *root) | 230 | btrfs_root *root) |
132 | { | 231 | { |
@@ -264,6 +363,8 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root | |||
264 | ret = btrfs_del_item(trans, extent_root, path); | 363 | ret = btrfs_del_item(trans, extent_root, path); |
265 | if (ret) | 364 | if (ret) |
266 | BUG(); | 365 | BUG(); |
366 | ret = update_block_group(trans, root, blocknr, num_blocks, 0); | ||
367 | BUG_ON(ret); | ||
267 | } | 368 | } |
268 | btrfs_release_path(extent_root, path); | 369 | btrfs_release_path(extent_root, path); |
269 | btrfs_free_path(path); | 370 | btrfs_free_path(path); |
@@ -365,21 +466,6 @@ static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root | |||
365 | num_blocks = 1; | 466 | num_blocks = 1; |
366 | total_needed = min(level + 2, BTRFS_MAX_LEVEL) * 3; | 467 | total_needed = min(level + 2, BTRFS_MAX_LEVEL) * 3; |
367 | } | 468 | } |
368 | if (info->last_insert.objectid == 0 && search_end == (u64)-1) { | ||
369 | struct btrfs_disk_key *last_key; | ||
370 | btrfs_init_path(path); | ||
371 | ins->objectid = (u64)-1; | ||
372 | ins->offset = (u64)-1; | ||
373 | ret = btrfs_search_slot(trans, root, ins, path, 0, 0); | ||
374 | if (ret < 0) | ||
375 | goto error; | ||
376 | BUG_ON(ret == 0); | ||
377 | if (path->slots[0] > 0) | ||
378 | path->slots[0]--; | ||
379 | l = btrfs_buffer_leaf(path->nodes[0]); | ||
380 | last_key = &l->items[path->slots[0]].key; | ||
381 | search_start = btrfs_disk_key_objectid(last_key); | ||
382 | } | ||
383 | if (info->last_insert.objectid > search_start) | 469 | if (info->last_insert.objectid > search_start) |
384 | search_start = info->last_insert.objectid; | 470 | search_start = info->last_insert.objectid; |
385 | 471 | ||
@@ -420,6 +506,8 @@ check_failed: | |||
420 | goto check_pending; | 506 | goto check_pending; |
421 | } | 507 | } |
422 | btrfs_disk_key_to_cpu(&key, &l->items[slot].key); | 508 | btrfs_disk_key_to_cpu(&key, &l->items[slot].key); |
509 | if (btrfs_key_type(&key) != BTRFS_EXTENT_ITEM_KEY) | ||
510 | goto next; | ||
423 | if (key.objectid >= search_start) { | 511 | if (key.objectid >= search_start) { |
424 | if (start_found) { | 512 | if (start_found) { |
425 | if (last_block < search_start) | 513 | if (last_block < search_start) |
@@ -434,6 +522,7 @@ check_failed: | |||
434 | } | 522 | } |
435 | start_found = 1; | 523 | start_found = 1; |
436 | last_block = key.objectid + key.offset; | 524 | last_block = key.objectid + key.offset; |
525 | next: | ||
437 | path->slots[0]++; | 526 | path->slots[0]++; |
438 | } | 527 | } |
439 | // FIXME -ENOSPC | 528 | // FIXME -ENOSPC |
@@ -498,7 +587,6 @@ error: | |||
498 | btrfs_free_path(path); | 587 | btrfs_free_path(path); |
499 | return ret; | 588 | return ret; |
500 | } | 589 | } |
501 | |||
502 | /* | 590 | /* |
503 | * finds a free extent and does all the dirty work required for allocation | 591 | * finds a free extent and does all the dirty work required for allocation |
504 | * returns the key for the extent through ins, and a tree buffer for | 592 | * returns the key for the extent through ins, and a tree buffer for |
@@ -532,6 +620,9 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, | |||
532 | ins->objectid = info->extent_tree_prealloc[nr]; | 620 | ins->objectid = info->extent_tree_prealloc[nr]; |
533 | info->extent_tree_insert[info->extent_tree_insert_nr++] = | 621 | info->extent_tree_insert[info->extent_tree_insert_nr++] = |
534 | ins->objectid; | 622 | ins->objectid; |
623 | ret = update_block_group(trans, root, | ||
624 | ins->objectid, ins->offset, 1); | ||
625 | BUG_ON(ret); | ||
535 | return 0; | 626 | return 0; |
536 | } | 627 | } |
537 | /* do the real allocation */ | 628 | /* do the real allocation */ |
@@ -558,6 +649,7 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, | |||
558 | return ret; | 649 | return ret; |
559 | if (pending_ret) | 650 | if (pending_ret) |
560 | return pending_ret; | 651 | return pending_ret; |
652 | ret = update_block_group(trans, root, ins->objectid, ins->offset, 1); | ||
561 | return 0; | 653 | return 0; |
562 | } | 654 | } |
563 | 655 | ||
@@ -578,6 +670,7 @@ struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, | |||
578 | BUG(); | 670 | BUG(); |
579 | return NULL; | 671 | return NULL; |
580 | } | 672 | } |
673 | BUG_ON(ret); | ||
581 | buf = btrfs_find_create_tree_block(root, ins.objectid); | 674 | buf = btrfs_find_create_tree_block(root, ins.objectid); |
582 | set_buffer_uptodate(buf); | 675 | set_buffer_uptodate(buf); |
583 | return buf; | 676 | return buf; |
@@ -758,3 +851,82 @@ int btrfs_drop_snapshot(struct btrfs_trans_handle *trans, struct btrfs_root | |||
758 | btrfs_free_path(path); | 851 | btrfs_free_path(path); |
759 | return ret; | 852 | return ret; |
760 | } | 853 | } |
854 | |||
855 | int btrfs_free_block_groups(struct btrfs_fs_info *info) | ||
856 | { | ||
857 | int ret; | ||
858 | struct btrfs_block_group_cache *cache[8]; | ||
859 | int i; | ||
860 | |||
861 | while(1) { | ||
862 | ret = radix_tree_gang_lookup(&info->block_group_radix, | ||
863 | (void **)cache, 0, | ||
864 | ARRAY_SIZE(cache)); | ||
865 | if (!ret) | ||
866 | break; | ||
867 | for (i = 0; i < ret; i++) { | ||
868 | radix_tree_delete(&info->block_group_radix, | ||
869 | cache[i]->key.objectid + | ||
870 | cache[i]->key.offset - 1); | ||
871 | kfree(cache[i]); | ||
872 | } | ||
873 | } | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | int btrfs_read_block_groups(struct btrfs_root *root) | ||
878 | { | ||
879 | struct btrfs_path *path; | ||
880 | int ret; | ||
881 | int err = 0; | ||
882 | struct btrfs_block_group_item *bi; | ||
883 | struct btrfs_block_group_cache *cache; | ||
884 | struct btrfs_key key; | ||
885 | struct btrfs_key found_key; | ||
886 | struct btrfs_leaf *leaf; | ||
887 | u64 group_size_blocks = BTRFS_BLOCK_GROUP_SIZE / root->blocksize; | ||
888 | |||
889 | root = root->fs_info->extent_root; | ||
890 | key.objectid = 0; | ||
891 | key.offset = group_size_blocks; | ||
892 | key.flags = 0; | ||
893 | btrfs_set_key_type(&key, BTRFS_BLOCK_GROUP_ITEM_KEY); | ||
894 | |||
895 | path = btrfs_alloc_path(); | ||
896 | if (!path) | ||
897 | return -ENOMEM; | ||
898 | |||
899 | while(1) { | ||
900 | ret = btrfs_search_slot(NULL, root->fs_info->extent_root, | ||
901 | &key, path, 0, 0); | ||
902 | if (ret != 0) { | ||
903 | err = ret; | ||
904 | break; | ||
905 | } | ||
906 | leaf = btrfs_buffer_leaf(path->nodes[0]); | ||
907 | btrfs_disk_key_to_cpu(&found_key, | ||
908 | &leaf->items[path->slots[0]].key); | ||
909 | cache = kmalloc(sizeof(*cache), GFP_NOFS); | ||
910 | if (!cache) { | ||
911 | err = -1; | ||
912 | break; | ||
913 | } | ||
914 | bi = btrfs_item_ptr(leaf, path->slots[0], | ||
915 | struct btrfs_block_group_item); | ||
916 | memcpy(&cache->item, bi, sizeof(*bi)); | ||
917 | memcpy(&cache->key, &found_key, sizeof(found_key)); | ||
918 | key.objectid = found_key.objectid + found_key.offset; | ||
919 | btrfs_release_path(root, path); | ||
920 | ret = radix_tree_insert(&root->fs_info->block_group_radix, | ||
921 | found_key.objectid + | ||
922 | found_key.offset - 1, | ||
923 | (void *)cache); | ||
924 | BUG_ON(ret); | ||
925 | if (key.objectid >= | ||
926 | btrfs_super_total_blocks(root->fs_info->disk_super)) | ||
927 | break; | ||
928 | } | ||
929 | |||
930 | btrfs_free_path(path); | ||
931 | return 0; | ||
932 | } | ||