diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-03-07 11:50:24 -0500 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-03-07 11:50:24 -0500 |
commit | 037e6390488af8ab96137e1e5cccc15ad14ef887 (patch) | |
tree | 8bcde2f20af3b7b0bdb37376d78902d1ea5e74fa /fs/btrfs/extent-tree.c | |
parent | a28ec19775d62d673b034082128aca95780d3737 (diff) |
Btrfs: get rid of add recursion
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 | 209 |
1 files changed, 95 insertions, 114 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 0723b7f3f0c3..8a2b8aaf9b86 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -6,6 +6,11 @@ | |||
6 | #include "disk-io.h" | 6 | #include "disk-io.h" |
7 | #include "print-tree.h" | 7 | #include "print-tree.h" |
8 | 8 | ||
9 | static int find_free_extent(struct ctree_root *orig_root, u64 num_blocks, | ||
10 | u64 search_start, u64 search_end, struct key *ins); | ||
11 | static int finish_current_insert(struct ctree_root *extent_root); | ||
12 | static int run_pending(struct ctree_root *extent_root); | ||
13 | |||
9 | /* | 14 | /* |
10 | * pending extents are blocks that we're trying to allocate in the extent | 15 | * pending extents are blocks that we're trying to allocate in the extent |
11 | * map while trying to grow the map because of other allocations. To avoid | 16 | * map while trying to grow the map because of other allocations. To avoid |
@@ -13,8 +18,7 @@ | |||
13 | * other allocations are done. The pending tag is also used in the same | 18 | * other allocations are done. The pending tag is also used in the same |
14 | * manner for deletes. | 19 | * manner for deletes. |
15 | */ | 20 | */ |
16 | #define CTREE_EXTENT_PENDING_ADD 0 | 21 | #define CTREE_EXTENT_PENDING_DEL 0 |
17 | #define CTREE_EXTENT_PENDING_DEL 1 | ||
18 | 22 | ||
19 | static int inc_block_ref(struct ctree_root *root, u64 blocknr) | 23 | static int inc_block_ref(struct ctree_root *root, u64 blocknr) |
20 | { | 24 | { |
@@ -23,6 +27,9 @@ static int inc_block_ref(struct ctree_root *root, u64 blocknr) | |||
23 | struct key key; | 27 | struct key key; |
24 | struct leaf *l; | 28 | struct leaf *l; |
25 | struct extent_item *item; | 29 | struct extent_item *item; |
30 | struct key ins; | ||
31 | |||
32 | find_free_extent(root->extent_root, 0, 0, (u64)-1, &ins); | ||
26 | init_path(&path); | 33 | init_path(&path); |
27 | key.objectid = blocknr; | 34 | key.objectid = blocknr; |
28 | key.flags = 0; | 35 | key.flags = 0; |
@@ -38,6 +45,8 @@ static int inc_block_ref(struct ctree_root *root, u64 blocknr) | |||
38 | 45 | ||
39 | BUG_ON(list_empty(&path.nodes[0]->dirty)); | 46 | BUG_ON(list_empty(&path.nodes[0]->dirty)); |
40 | release_path(root->extent_root, &path); | 47 | release_path(root->extent_root, &path); |
48 | finish_current_insert(root->extent_root); | ||
49 | run_pending(root->extent_root); | ||
41 | return 0; | 50 | return 0; |
42 | } | 51 | } |
43 | 52 | ||
@@ -99,6 +108,28 @@ int btrfs_finish_extent_commit(struct ctree_root *root) | |||
99 | return 0; | 108 | return 0; |
100 | } | 109 | } |
101 | 110 | ||
111 | static int finish_current_insert(struct ctree_root *extent_root) | ||
112 | { | ||
113 | struct key ins; | ||
114 | struct extent_item extent_item; | ||
115 | int i; | ||
116 | int ret; | ||
117 | |||
118 | extent_item.refs = 1; | ||
119 | extent_item.owner = extent_root->node->node.header.parentid; | ||
120 | ins.offset = 1; | ||
121 | ins.flags = 0; | ||
122 | |||
123 | for (i = 0; i < extent_root->current_insert.flags; i++) { | ||
124 | ins.objectid = extent_root->current_insert.objectid + i; | ||
125 | ret = insert_item(extent_root, &ins, &extent_item, | ||
126 | sizeof(extent_item)); | ||
127 | BUG_ON(ret); | ||
128 | } | ||
129 | extent_root->current_insert.offset = 0; | ||
130 | return 0; | ||
131 | } | ||
132 | |||
102 | /* | 133 | /* |
103 | * remove an extent from the root, returns 0 on success | 134 | * remove an extent from the root, returns 0 on success |
104 | */ | 135 | */ |
@@ -110,10 +141,13 @@ int __free_extent(struct ctree_root *root, u64 blocknr, u64 num_blocks) | |||
110 | int ret; | 141 | int ret; |
111 | struct item *item; | 142 | struct item *item; |
112 | struct extent_item *ei; | 143 | struct extent_item *ei; |
144 | struct key ins; | ||
145 | |||
113 | key.objectid = blocknr; | 146 | key.objectid = blocknr; |
114 | key.flags = 0; | 147 | key.flags = 0; |
115 | key.offset = num_blocks; | 148 | key.offset = num_blocks; |
116 | 149 | ||
150 | find_free_extent(root, 0, 0, (u64)-1, &ins); | ||
117 | init_path(&path); | 151 | init_path(&path); |
118 | ret = search_slot(extent_root, &key, &path, -1, 1); | 152 | ret = search_slot(extent_root, &key, &path, -1, 1); |
119 | if (ret) { | 153 | if (ret) { |
@@ -140,54 +174,11 @@ int __free_extent(struct ctree_root *root, u64 blocknr, u64 num_blocks) | |||
140 | BUG(); | 174 | BUG(); |
141 | } | 175 | } |
142 | release_path(extent_root, &path); | 176 | release_path(extent_root, &path); |
177 | finish_current_insert(extent_root); | ||
143 | return ret; | 178 | return ret; |
144 | } | 179 | } |
145 | 180 | ||
146 | /* | 181 | /* |
147 | * insert all of the pending extents reserved during the original | ||
148 | * allocation. (CTREE_EXTENT_PENDING). Returns zero if it all worked out | ||
149 | */ | ||
150 | static int insert_pending_extents(struct ctree_root *extent_root) | ||
151 | { | ||
152 | int ret; | ||
153 | struct key key; | ||
154 | struct extent_item item; | ||
155 | struct tree_buffer *gang[4]; | ||
156 | int i; | ||
157 | |||
158 | // FIXME -ENOSPC | ||
159 | item.owner = extent_root->node->node.header.parentid; | ||
160 | item.refs = 1; | ||
161 | while(1) { | ||
162 | ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix, | ||
163 | (void **)gang, 0, | ||
164 | ARRAY_SIZE(gang), | ||
165 | CTREE_EXTENT_PENDING_ADD); | ||
166 | if (!ret) | ||
167 | break; | ||
168 | for (i = 0; i < ret; i++) { | ||
169 | key.objectid = gang[i]->blocknr; | ||
170 | key.flags = 0; | ||
171 | key.offset = 1; | ||
172 | ret = insert_item(extent_root, &key, &item, | ||
173 | sizeof(item)); | ||
174 | if (ret) { | ||
175 | printf("%Lu already in tree\n", key.objectid); | ||
176 | print_tree(extent_root, extent_root->node); | ||
177 | BUG(); | ||
178 | // FIXME undo it and return sane | ||
179 | return ret; | ||
180 | } | ||
181 | radix_tree_tag_clear(&extent_root->cache_radix, | ||
182 | gang[i]->blocknr, | ||
183 | CTREE_EXTENT_PENDING_ADD); | ||
184 | tree_block_release(extent_root, gang[i]); | ||
185 | } | ||
186 | } | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * find all the blocks marked as pending in the radix tree and remove | 182 | * find all the blocks marked as pending in the radix tree and remove |
192 | * them from the extent map | 183 | * them from the extent map |
193 | */ | 184 | */ |
@@ -218,12 +209,8 @@ static int del_pending_extents(struct ctree_root *extent_root) | |||
218 | static int run_pending(struct ctree_root *extent_root) | 209 | static int run_pending(struct ctree_root *extent_root) |
219 | { | 210 | { |
220 | while(radix_tree_tagged(&extent_root->cache_radix, | 211 | while(radix_tree_tagged(&extent_root->cache_radix, |
221 | CTREE_EXTENT_PENDING_DEL) || | 212 | CTREE_EXTENT_PENDING_DEL)) |
222 | radix_tree_tagged(&extent_root->cache_radix, | ||
223 | CTREE_EXTENT_PENDING_ADD)) { | ||
224 | insert_pending_extents(extent_root); | ||
225 | del_pending_extents(extent_root); | 213 | del_pending_extents(extent_root); |
226 | } | ||
227 | return 0; | 214 | return 0; |
228 | } | 215 | } |
229 | 216 | ||
@@ -241,19 +228,8 @@ int free_extent(struct ctree_root *root, u64 blocknr, u64 num_blocks) | |||
241 | 228 | ||
242 | if (root == extent_root) { | 229 | if (root == extent_root) { |
243 | t = find_tree_block(root, blocknr); | 230 | t = find_tree_block(root, blocknr); |
244 | if (radix_tree_tag_get(&root->cache_radix, blocknr, | 231 | radix_tree_tag_set(&root->cache_radix, blocknr, |
245 | CTREE_EXTENT_PENDING_ADD)) { | ||
246 | radix_tree_tag_clear(&root->cache_radix, | ||
247 | blocknr, | ||
248 | CTREE_EXTENT_PENDING_ADD); | ||
249 | /* once for us */ | ||
250 | tree_block_release(root, t); | ||
251 | /* once for the pending add */ | ||
252 | tree_block_release(root, t); | ||
253 | } else { | ||
254 | radix_tree_tag_set(&root->cache_radix, blocknr, | ||
255 | CTREE_EXTENT_PENDING_DEL); | 232 | CTREE_EXTENT_PENDING_DEL); |
256 | } | ||
257 | return 0; | 233 | return 0; |
258 | } | 234 | } |
259 | key.objectid = blocknr; | 235 | key.objectid = blocknr; |
@@ -281,9 +257,11 @@ static int find_free_extent(struct ctree_root *orig_root, u64 num_blocks, | |||
281 | u64 hole_size = 0; | 257 | u64 hole_size = 0; |
282 | int slot = 0; | 258 | int slot = 0; |
283 | u64 last_block; | 259 | u64 last_block; |
260 | u64 test_block; | ||
284 | int start_found; | 261 | int start_found; |
285 | struct leaf *l; | 262 | struct leaf *l; |
286 | struct ctree_root * root = orig_root->extent_root; | 263 | struct ctree_root * root = orig_root->extent_root; |
264 | int total_needed = num_blocks + MAX_LEVEL * 3; | ||
287 | 265 | ||
288 | check_failed: | 266 | check_failed: |
289 | init_path(&path); | 267 | init_path(&path); |
@@ -306,22 +284,34 @@ check_failed: | |||
306 | goto error; | 284 | goto error; |
307 | if (!start_found) { | 285 | if (!start_found) { |
308 | ins->objectid = search_start; | 286 | ins->objectid = search_start; |
309 | ins->offset = num_blocks; | 287 | ins->offset = (u64)-1; |
310 | start_found = 1; | 288 | start_found = 1; |
311 | goto check_pending; | 289 | goto check_pending; |
312 | } | 290 | } |
313 | ins->objectid = last_block > search_start ? | 291 | ins->objectid = last_block > search_start ? |
314 | last_block : search_start; | 292 | last_block : search_start; |
315 | ins->offset = num_blocks; | 293 | ins->offset = (u64)-1; |
316 | goto check_pending; | 294 | goto check_pending; |
317 | } | 295 | } |
296 | if (slot == 0) { | ||
297 | int last_slot = l->header.nritems - 1; | ||
298 | u64 span = l->items[last_slot].key.objectid; | ||
299 | span -= l->items[slot].key.objectid; | ||
300 | if (span + total_needed > last_slot - slot) { | ||
301 | path.slots[0] = last_slot + 1; | ||
302 | key = &l->items[last_slot].key; | ||
303 | last_block = key->objectid + key->offset; | ||
304 | start_found = 1; | ||
305 | continue; | ||
306 | } | ||
307 | } | ||
318 | key = &l->items[slot].key; | 308 | key = &l->items[slot].key; |
319 | if (key->objectid >= search_start) { | 309 | if (key->objectid >= search_start) { |
320 | if (start_found) { | 310 | if (start_found) { |
321 | hole_size = key->objectid - last_block; | 311 | hole_size = key->objectid - last_block; |
322 | if (hole_size > num_blocks) { | 312 | if (hole_size > total_needed) { |
323 | ins->objectid = last_block; | 313 | ins->objectid = last_block; |
324 | ins->offset = num_blocks; | 314 | ins->offset = hole_size; |
325 | goto check_pending; | 315 | goto check_pending; |
326 | } | 316 | } |
327 | } else | 317 | } else |
@@ -337,23 +327,18 @@ check_pending: | |||
337 | */ | 327 | */ |
338 | release_path(root, &path); | 328 | release_path(root, &path); |
339 | BUG_ON(ins->objectid < search_start); | 329 | BUG_ON(ins->objectid < search_start); |
340 | if (1 || orig_root->extent_root == orig_root) { | 330 | for (test_block = ins->objectid; |
341 | BUG_ON(num_blocks != 1); | 331 | test_block < ins->objectid + total_needed; test_block++) { |
342 | if ((root->current_insert.objectid <= ins->objectid && | 332 | if (radix_tree_lookup(&root->pinned_radix, test_block)) { |
343 | root->current_insert.objectid + | 333 | search_start = test_block + 1; |
344 | root->current_insert.offset > ins->objectid) || | ||
345 | (root->current_insert.objectid > ins->objectid && | ||
346 | root->current_insert.objectid <= ins->objectid + | ||
347 | ins->offset) || | ||
348 | radix_tree_lookup(&root->pinned_radix, ins->objectid) || | ||
349 | radix_tree_tag_get(&root->cache_radix, ins->objectid, | ||
350 | CTREE_EXTENT_PENDING_ADD)) { | ||
351 | search_start = ins->objectid + 1; | ||
352 | goto check_failed; | 334 | goto check_failed; |
353 | } | 335 | } |
354 | } | 336 | } |
355 | if (ins->offset != 1) | 337 | BUG_ON(root->current_insert.offset); |
356 | BUG(); | 338 | root->current_insert.offset = total_needed; |
339 | root->current_insert.objectid = ins->objectid + num_blocks; | ||
340 | root->current_insert.flags = 0; | ||
341 | ins->offset = num_blocks; | ||
357 | return 0; | 342 | return 0; |
358 | error: | 343 | error: |
359 | release_path(root, &path); | 344 | release_path(root, &path); |
@@ -368,43 +353,41 @@ error: | |||
368 | * returns 0 if everything worked, non-zero otherwise. | 353 | * returns 0 if everything worked, non-zero otherwise. |
369 | */ | 354 | */ |
370 | int alloc_extent(struct ctree_root *root, u64 num_blocks, u64 search_start, | 355 | int alloc_extent(struct ctree_root *root, u64 num_blocks, u64 search_start, |
371 | u64 search_end, u64 owner, struct key *ins, | 356 | u64 search_end, u64 owner, struct key *ins) |
372 | struct tree_buffer **buf) | ||
373 | { | 357 | { |
374 | int ret; | 358 | int ret; |
375 | int pending_ret; | 359 | int pending_ret; |
360 | struct ctree_root *extent_root = root->extent_root; | ||
376 | struct extent_item extent_item; | 361 | struct extent_item extent_item; |
362 | |||
377 | extent_item.refs = 1; | 363 | extent_item.refs = 1; |
378 | extent_item.owner = owner; | 364 | extent_item.owner = owner; |
379 | 365 | ||
380 | ret = find_free_extent(root, num_blocks, search_start, search_end, ins); | 366 | if (root == extent_root) { |
381 | if (ret) | 367 | BUG_ON(extent_root->current_insert.offset == 0); |
382 | return ret; | 368 | BUG_ON(num_blocks != 1); |
383 | if (root != root->extent_root) { | 369 | BUG_ON(extent_root->current_insert.flags == |
384 | memcpy(&root->extent_root->current_insert, ins, sizeof(*ins)); | 370 | extent_root->current_insert.offset); |
385 | ret = insert_item(root->extent_root, ins, &extent_item, | 371 | ins->offset = 1; |
386 | sizeof(extent_item)); | 372 | ins->objectid = extent_root->current_insert.objectid + |
387 | memset(&root->extent_root->current_insert, 0, | 373 | extent_root->current_insert.flags++; |
388 | sizeof(struct key)); | ||
389 | pending_ret = run_pending(root->extent_root); | ||
390 | if (ret) | ||
391 | return ret; | ||
392 | if (pending_ret) | ||
393 | return pending_ret; | ||
394 | *buf = find_tree_block(root, ins->objectid); | ||
395 | dirty_tree_block(root, *buf); | ||
396 | return 0; | 374 | return 0; |
397 | } | 375 | } |
398 | /* we're allocating an extent for the extent tree, don't recurse */ | 376 | ret = find_free_extent(root, num_blocks, search_start, |
399 | BUG_ON(ins->offset != 1); | 377 | search_end, ins); |
400 | *buf = find_tree_block(root, ins->objectid); | 378 | if (ret) |
401 | BUG_ON(!*buf); | 379 | return ret; |
402 | radix_tree_tag_set(&root->cache_radix, ins->objectid, | ||
403 | CTREE_EXTENT_PENDING_ADD); | ||
404 | (*buf)->count++; | ||
405 | dirty_tree_block(root, *buf); | ||
406 | return 0; | ||
407 | 380 | ||
381 | ret = insert_item(extent_root, ins, &extent_item, | ||
382 | sizeof(extent_item)); | ||
383 | |||
384 | finish_current_insert(extent_root); | ||
385 | pending_ret = run_pending(extent_root); | ||
386 | if (ret) | ||
387 | return ret; | ||
388 | if (pending_ret) | ||
389 | return pending_ret; | ||
390 | return 0; | ||
408 | } | 391 | } |
409 | 392 | ||
410 | /* | 393 | /* |
@@ -415,19 +398,17 @@ struct tree_buffer *alloc_free_block(struct ctree_root *root) | |||
415 | { | 398 | { |
416 | struct key ins; | 399 | struct key ins; |
417 | int ret; | 400 | int ret; |
418 | struct tree_buffer *buf = NULL; | 401 | struct tree_buffer *buf; |
419 | 402 | ||
420 | ret = alloc_extent(root, 1, 0, (unsigned long)-1, | 403 | ret = alloc_extent(root, 1, 0, (unsigned long)-1, |
421 | root->node->node.header.parentid, | 404 | root->node->node.header.parentid, |
422 | &ins, &buf); | 405 | &ins); |
423 | if (ret) { | 406 | if (ret) { |
424 | BUG(); | 407 | BUG(); |
425 | return NULL; | 408 | return NULL; |
426 | } | 409 | } |
427 | if (root != root->extent_root) | 410 | buf = find_tree_block(root, ins.objectid); |
428 | BUG_ON(radix_tree_tag_get(&root->extent_root->cache_radix, | 411 | dirty_tree_block(root, buf); |
429 | buf->blocknr, | ||
430 | CTREE_EXTENT_PENDING_ADD)); | ||
431 | return buf; | 412 | return buf; |
432 | } | 413 | } |
433 | 414 | ||