diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-03-06 20:08:01 -0500 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-03-06 20:08:01 -0500 |
commit | a28ec19775d62d673b034082128aca95780d3737 (patch) | |
tree | 105694ca29ed0ad6563c45a86df9a7df1abf94ff /fs/btrfs/extent-tree.c | |
parent | 02217ed299c6340a35696e0610047eb96826de2d (diff) |
Btrfs: Fixup reference counting on cows
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 | 291 |
1 files changed, 204 insertions, 87 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 25d9cd169209..0723b7f3f0c3 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -13,7 +13,8 @@ | |||
13 | * other allocations are done. The pending tag is also used in the same | 13 | * other allocations are done. The pending tag is also used in the same |
14 | * manner for deletes. | 14 | * manner for deletes. |
15 | */ | 15 | */ |
16 | #define CTREE_EXTENT_PENDING 0 | 16 | #define CTREE_EXTENT_PENDING_ADD 0 |
17 | #define CTREE_EXTENT_PENDING_DEL 1 | ||
17 | 18 | ||
18 | static int inc_block_ref(struct ctree_root *root, u64 blocknr) | 19 | static int inc_block_ref(struct ctree_root *root, u64 blocknr) |
19 | { | 20 | { |
@@ -27,20 +28,51 @@ static int inc_block_ref(struct ctree_root *root, u64 blocknr) | |||
27 | key.flags = 0; | 28 | key.flags = 0; |
28 | key.offset = 1; | 29 | key.offset = 1; |
29 | ret = search_slot(root->extent_root, &key, &path, 0, 1); | 30 | ret = search_slot(root->extent_root, &key, &path, 0, 1); |
31 | if (ret != 0) | ||
32 | BUG(); | ||
30 | BUG_ON(ret != 0); | 33 | BUG_ON(ret != 0); |
31 | l = &path.nodes[0]->leaf; | 34 | l = &path.nodes[0]->leaf; |
32 | item = (struct extent_item *)(l->data + | 35 | item = (struct extent_item *)(l->data + |
33 | l->items[path.slots[0]].offset); | 36 | l->items[path.slots[0]].offset); |
34 | item->refs++; | 37 | item->refs++; |
38 | |||
35 | BUG_ON(list_empty(&path.nodes[0]->dirty)); | 39 | BUG_ON(list_empty(&path.nodes[0]->dirty)); |
36 | release_path(root->extent_root, &path); | 40 | release_path(root->extent_root, &path); |
37 | return 0; | 41 | return 0; |
38 | } | 42 | } |
39 | 43 | ||
44 | static int lookup_block_ref(struct ctree_root *root, u64 blocknr, int *refs) | ||
45 | { | ||
46 | struct ctree_path path; | ||
47 | int ret; | ||
48 | struct key key; | ||
49 | struct leaf *l; | ||
50 | struct extent_item *item; | ||
51 | init_path(&path); | ||
52 | key.objectid = blocknr; | ||
53 | key.flags = 0; | ||
54 | key.offset = 1; | ||
55 | ret = search_slot(root->extent_root, &key, &path, 0, 0); | ||
56 | if (ret != 0) | ||
57 | BUG(); | ||
58 | l = &path.nodes[0]->leaf; | ||
59 | item = (struct extent_item *)(l->data + | ||
60 | l->items[path.slots[0]].offset); | ||
61 | *refs = item->refs; | ||
62 | release_path(root->extent_root, &path); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
40 | int btrfs_inc_ref(struct ctree_root *root, struct tree_buffer *buf) | 66 | int btrfs_inc_ref(struct ctree_root *root, struct tree_buffer *buf) |
41 | { | 67 | { |
42 | u64 blocknr; | 68 | u64 blocknr; |
43 | int i; | 69 | int i; |
70 | |||
71 | if (root == root->extent_root) | ||
72 | return 0; | ||
73 | if (is_leaf(buf->node.header.flags)) | ||
74 | return 0; | ||
75 | |||
44 | for (i = 0; i < buf->node.header.nritems; i++) { | 76 | for (i = 0; i < buf->node.header.nritems; i++) { |
45 | blocknr = buf->node.blockptrs[i]; | 77 | blocknr = buf->node.blockptrs[i]; |
46 | inc_block_ref(root, blocknr); | 78 | inc_block_ref(root, blocknr); |
@@ -48,85 +80,187 @@ int btrfs_inc_ref(struct ctree_root *root, struct tree_buffer *buf) | |||
48 | return 0; | 80 | return 0; |
49 | } | 81 | } |
50 | 82 | ||
83 | int btrfs_finish_extent_commit(struct ctree_root *root) | ||
84 | { | ||
85 | struct ctree_root *extent_root = root->extent_root; | ||
86 | unsigned long gang[8]; | ||
87 | int ret; | ||
88 | int i; | ||
89 | |||
90 | while(1) { | ||
91 | ret = radix_tree_gang_lookup(&extent_root->pinned_radix, | ||
92 | (void **)gang, 0, | ||
93 | ARRAY_SIZE(gang)); | ||
94 | if (!ret) | ||
95 | break; | ||
96 | for (i = 0; i < ret; i++) | ||
97 | radix_tree_delete(&extent_root->pinned_radix, gang[i]); | ||
98 | } | ||
99 | return 0; | ||
100 | } | ||
101 | |||
51 | /* | 102 | /* |
52 | * find all the blocks marked as pending in the radix tree and remove | 103 | * remove an extent from the root, returns 0 on success |
53 | * them from the extent map | ||
54 | */ | 104 | */ |
55 | static int del_pending_extents(struct ctree_root *extent_root) | 105 | int __free_extent(struct ctree_root *root, u64 blocknr, u64 num_blocks) |
106 | { | ||
107 | struct ctree_path path; | ||
108 | struct key key; | ||
109 | struct ctree_root *extent_root = root->extent_root; | ||
110 | int ret; | ||
111 | struct item *item; | ||
112 | struct extent_item *ei; | ||
113 | key.objectid = blocknr; | ||
114 | key.flags = 0; | ||
115 | key.offset = num_blocks; | ||
116 | |||
117 | init_path(&path); | ||
118 | ret = search_slot(extent_root, &key, &path, -1, 1); | ||
119 | if (ret) { | ||
120 | printf("failed to find %Lu\n", key.objectid); | ||
121 | print_tree(extent_root, extent_root->node); | ||
122 | printf("failed to find %Lu\n", key.objectid); | ||
123 | BUG(); | ||
124 | } | ||
125 | item = path.nodes[0]->leaf.items + path.slots[0]; | ||
126 | ei = (struct extent_item *)(path.nodes[0]->leaf.data + item->offset); | ||
127 | BUG_ON(ei->refs == 0); | ||
128 | ei->refs--; | ||
129 | if (ei->refs == 0) { | ||
130 | if (root == extent_root) { | ||
131 | int err; | ||
132 | radix_tree_preload(GFP_KERNEL); | ||
133 | err = radix_tree_insert(&extent_root->pinned_radix, | ||
134 | blocknr, (void *)blocknr); | ||
135 | BUG_ON(err); | ||
136 | radix_tree_preload_end(); | ||
137 | } | ||
138 | ret = del_item(extent_root, &path); | ||
139 | if (ret) | ||
140 | BUG(); | ||
141 | } | ||
142 | release_path(extent_root, &path); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | /* | ||
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) | ||
56 | { | 151 | { |
57 | int ret; | 152 | int ret; |
58 | struct key key; | 153 | struct key key; |
154 | struct extent_item item; | ||
59 | struct tree_buffer *gang[4]; | 155 | struct tree_buffer *gang[4]; |
60 | int i; | 156 | int i; |
61 | struct ctree_path path; | ||
62 | 157 | ||
158 | // FIXME -ENOSPC | ||
159 | item.owner = extent_root->node->node.header.parentid; | ||
160 | item.refs = 1; | ||
63 | while(1) { | 161 | while(1) { |
64 | ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix, | 162 | ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix, |
65 | (void **)gang, 0, | 163 | (void **)gang, 0, |
66 | ARRAY_SIZE(gang), | 164 | ARRAY_SIZE(gang), |
67 | CTREE_EXTENT_PENDING); | 165 | CTREE_EXTENT_PENDING_ADD); |
68 | if (!ret) | 166 | if (!ret) |
69 | break; | 167 | break; |
70 | for (i = 0; i < ret; i++) { | 168 | for (i = 0; i < ret; i++) { |
71 | key.objectid = gang[i]->blocknr; | 169 | key.objectid = gang[i]->blocknr; |
72 | key.flags = 0; | 170 | key.flags = 0; |
73 | key.offset = 1; | 171 | key.offset = 1; |
74 | init_path(&path); | 172 | ret = insert_item(extent_root, &key, &item, |
75 | ret = search_slot(extent_root, &key, &path, -1, 1); | 173 | sizeof(item)); |
76 | if (ret) { | 174 | if (ret) { |
175 | printf("%Lu already in tree\n", key.objectid); | ||
77 | print_tree(extent_root, extent_root->node); | 176 | print_tree(extent_root, extent_root->node); |
78 | printf("unable to find %Lu\n", key.objectid); | ||
79 | BUG(); | 177 | BUG(); |
80 | // FIXME undo it and return sane | 178 | // FIXME undo it and return sane |
81 | return ret; | 179 | return ret; |
82 | } | 180 | } |
83 | ret = del_item(extent_root, &path); | 181 | radix_tree_tag_clear(&extent_root->cache_radix, |
84 | if (ret) { | 182 | gang[i]->blocknr, |
85 | BUG(); | 183 | CTREE_EXTENT_PENDING_ADD); |
86 | return ret; | 184 | tree_block_release(extent_root, gang[i]); |
87 | } | 185 | } |
88 | release_path(extent_root, &path); | 186 | } |
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * find all the blocks marked as pending in the radix tree and remove | ||
192 | * them from the extent map | ||
193 | */ | ||
194 | static int del_pending_extents(struct ctree_root *extent_root) | ||
195 | { | ||
196 | int ret; | ||
197 | struct tree_buffer *gang[4]; | ||
198 | int i; | ||
199 | |||
200 | while(1) { | ||
201 | ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix, | ||
202 | (void **)gang, 0, | ||
203 | ARRAY_SIZE(gang), | ||
204 | CTREE_EXTENT_PENDING_DEL); | ||
205 | if (!ret) | ||
206 | break; | ||
207 | for (i = 0; i < ret; i++) { | ||
208 | ret = __free_extent(extent_root, gang[i]->blocknr, 1); | ||
89 | radix_tree_tag_clear(&extent_root->cache_radix, | 209 | radix_tree_tag_clear(&extent_root->cache_radix, |
90 | gang[i]->blocknr, | 210 | gang[i]->blocknr, |
91 | CTREE_EXTENT_PENDING); | 211 | CTREE_EXTENT_PENDING_DEL); |
92 | tree_block_release(extent_root, gang[i]); | 212 | tree_block_release(extent_root, gang[i]); |
93 | } | 213 | } |
94 | } | 214 | } |
95 | return 0; | 215 | return 0; |
96 | } | 216 | } |
97 | 217 | ||
218 | static int run_pending(struct ctree_root *extent_root) | ||
219 | { | ||
220 | while(radix_tree_tagged(&extent_root->cache_radix, | ||
221 | 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); | ||
226 | } | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | |||
98 | /* | 231 | /* |
99 | * remove an extent from the root, returns 0 on success | 232 | * remove an extent from the root, returns 0 on success |
100 | */ | 233 | */ |
101 | int free_extent(struct ctree_root *root, u64 blocknr, u64 num_blocks) | 234 | int free_extent(struct ctree_root *root, u64 blocknr, u64 num_blocks) |
102 | { | 235 | { |
103 | struct ctree_path path; | ||
104 | struct key key; | 236 | struct key key; |
105 | struct ctree_root *extent_root = root->extent_root; | 237 | struct ctree_root *extent_root = root->extent_root; |
106 | struct tree_buffer *t; | 238 | struct tree_buffer *t; |
107 | int pending_ret; | 239 | int pending_ret; |
108 | int ret; | 240 | int ret; |
109 | key.objectid = blocknr; | 241 | |
110 | key.flags = 0; | ||
111 | key.offset = num_blocks; | ||
112 | if (root == extent_root) { | 242 | if (root == extent_root) { |
113 | t = read_tree_block(root, key.objectid); | 243 | t = find_tree_block(root, blocknr); |
114 | radix_tree_tag_set(&root->cache_radix, key.objectid, | 244 | if (radix_tree_tag_get(&root->cache_radix, blocknr, |
115 | CTREE_EXTENT_PENDING); | 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); | ||
256 | } | ||
116 | return 0; | 257 | return 0; |
117 | } | 258 | } |
118 | init_path(&path); | 259 | key.objectid = blocknr; |
119 | ret = search_slot(extent_root, &key, &path, -1, 1); | 260 | key.flags = 0; |
120 | if (ret) { | 261 | key.offset = num_blocks; |
121 | print_tree(extent_root, extent_root->node); | 262 | ret = __free_extent(root, blocknr, num_blocks); |
122 | printf("failed to find %Lu\n", key.objectid); | 263 | pending_ret = run_pending(root->extent_root); |
123 | BUG(); | ||
124 | } | ||
125 | ret = del_item(extent_root, &path); | ||
126 | if (ret) | ||
127 | BUG(); | ||
128 | release_path(extent_root, &path); | ||
129 | pending_ret = del_pending_extents(root->extent_root); | ||
130 | return ret ? ret : pending_ret; | 264 | return ret ? ret : pending_ret; |
131 | } | 265 | } |
132 | 266 | ||
@@ -203,7 +337,7 @@ check_pending: | |||
203 | */ | 337 | */ |
204 | release_path(root, &path); | 338 | release_path(root, &path); |
205 | BUG_ON(ins->objectid < search_start); | 339 | BUG_ON(ins->objectid < search_start); |
206 | if (orig_root->extent_root == orig_root) { | 340 | if (1 || orig_root->extent_root == orig_root) { |
207 | BUG_ON(num_blocks != 1); | 341 | BUG_ON(num_blocks != 1); |
208 | if ((root->current_insert.objectid <= ins->objectid && | 342 | if ((root->current_insert.objectid <= ins->objectid && |
209 | root->current_insert.objectid + | 343 | root->current_insert.objectid + |
@@ -211,8 +345,9 @@ check_pending: | |||
211 | (root->current_insert.objectid > ins->objectid && | 345 | (root->current_insert.objectid > ins->objectid && |
212 | root->current_insert.objectid <= ins->objectid + | 346 | root->current_insert.objectid <= ins->objectid + |
213 | ins->offset) || | 347 | ins->offset) || |
348 | radix_tree_lookup(&root->pinned_radix, ins->objectid) || | ||
214 | radix_tree_tag_get(&root->cache_radix, ins->objectid, | 349 | radix_tree_tag_get(&root->cache_radix, ins->objectid, |
215 | CTREE_EXTENT_PENDING)) { | 350 | CTREE_EXTENT_PENDING_ADD)) { |
216 | search_start = ins->objectid + 1; | 351 | search_start = ins->objectid + 1; |
217 | goto check_failed; | 352 | goto check_failed; |
218 | } | 353 | } |
@@ -226,51 +361,6 @@ error: | |||
226 | } | 361 | } |
227 | 362 | ||
228 | /* | 363 | /* |
229 | * insert all of the pending extents reserved during the original | ||
230 | * allocation. (CTREE_EXTENT_PENDING). Returns zero if it all worked out | ||
231 | */ | ||
232 | static int insert_pending_extents(struct ctree_root *extent_root) | ||
233 | { | ||
234 | int ret; | ||
235 | struct key key; | ||
236 | struct extent_item item; | ||
237 | struct tree_buffer *gang[4]; | ||
238 | int i; | ||
239 | |||
240 | // FIXME -ENOSPC | ||
241 | item.refs = 1; | ||
242 | item.owner = extent_root->node->node.header.parentid; | ||
243 | while(1) { | ||
244 | ret = radix_tree_gang_lookup_tag(&extent_root->cache_radix, | ||
245 | (void **)gang, 0, | ||
246 | ARRAY_SIZE(gang), | ||
247 | CTREE_EXTENT_PENDING); | ||
248 | if (!ret) | ||
249 | break; | ||
250 | for (i = 0; i < ret; i++) { | ||
251 | key.objectid = gang[i]->blocknr; | ||
252 | key.flags = 0; | ||
253 | key.offset = 1; | ||
254 | ret = insert_item(extent_root, &key, &item, | ||
255 | sizeof(item)); | ||
256 | if (ret) { | ||
257 | printf("%Lu already in tree\n", key.objectid); | ||
258 | print_tree(extent_root, extent_root->node); | ||
259 | BUG(); | ||
260 | // FIXME undo it and return sane | ||
261 | return ret; | ||
262 | } | ||
263 | radix_tree_tag_clear(&extent_root->cache_radix, | ||
264 | gang[i]->blocknr, | ||
265 | CTREE_EXTENT_PENDING); | ||
266 | printf("%Lu is not pending\n", gang[i]->blocknr); | ||
267 | tree_block_release(extent_root, gang[i]); | ||
268 | } | ||
269 | } | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * finds a free extent and does all the dirty work required for allocation | 364 | * finds a free extent and does all the dirty work required for allocation |
275 | * returns the key for the extent through ins, and a tree buffer for | 365 | * returns the key for the extent through ins, and a tree buffer for |
276 | * the first block of the extent through buf. | 366 | * the first block of the extent through buf. |
@@ -296,7 +386,7 @@ int alloc_extent(struct ctree_root *root, u64 num_blocks, u64 search_start, | |||
296 | sizeof(extent_item)); | 386 | sizeof(extent_item)); |
297 | memset(&root->extent_root->current_insert, 0, | 387 | memset(&root->extent_root->current_insert, 0, |
298 | sizeof(struct key)); | 388 | sizeof(struct key)); |
299 | pending_ret = insert_pending_extents(root->extent_root); | 389 | pending_ret = run_pending(root->extent_root); |
300 | if (ret) | 390 | if (ret) |
301 | return ret; | 391 | return ret; |
302 | if (pending_ret) | 392 | if (pending_ret) |
@@ -309,9 +399,8 @@ int alloc_extent(struct ctree_root *root, u64 num_blocks, u64 search_start, | |||
309 | BUG_ON(ins->offset != 1); | 399 | BUG_ON(ins->offset != 1); |
310 | *buf = find_tree_block(root, ins->objectid); | 400 | *buf = find_tree_block(root, ins->objectid); |
311 | BUG_ON(!*buf); | 401 | BUG_ON(!*buf); |
312 | printf("%Lu is pending\n", ins->objectid); | ||
313 | radix_tree_tag_set(&root->cache_radix, ins->objectid, | 402 | radix_tree_tag_set(&root->cache_radix, ins->objectid, |
314 | CTREE_EXTENT_PENDING); | 403 | CTREE_EXTENT_PENDING_ADD); |
315 | (*buf)->count++; | 404 | (*buf)->count++; |
316 | dirty_tree_block(root, *buf); | 405 | dirty_tree_block(root, *buf); |
317 | return 0; | 406 | return 0; |
@@ -331,13 +420,41 @@ struct tree_buffer *alloc_free_block(struct ctree_root *root) | |||
331 | ret = alloc_extent(root, 1, 0, (unsigned long)-1, | 420 | ret = alloc_extent(root, 1, 0, (unsigned long)-1, |
332 | root->node->node.header.parentid, | 421 | root->node->node.header.parentid, |
333 | &ins, &buf); | 422 | &ins, &buf); |
334 | |||
335 | if (ret) { | 423 | if (ret) { |
336 | BUG(); | 424 | BUG(); |
337 | return NULL; | 425 | return NULL; |
338 | } | 426 | } |
339 | if (root != root->extent_root) | 427 | if (root != root->extent_root) |
340 | BUG_ON(radix_tree_tag_get(&root->extent_root->cache_radix, | 428 | BUG_ON(radix_tree_tag_get(&root->extent_root->cache_radix, |
341 | buf->blocknr, CTREE_EXTENT_PENDING)); | 429 | buf->blocknr, |
430 | CTREE_EXTENT_PENDING_ADD)); | ||
342 | return buf; | 431 | return buf; |
343 | } | 432 | } |
433 | |||
434 | int btrfs_drop_snapshot(struct ctree_root *root, struct tree_buffer *snap) | ||
435 | { | ||
436 | int ret; | ||
437 | int level; | ||
438 | int refs; | ||
439 | u64 blocknr = snap->blocknr; | ||
440 | |||
441 | level = node_level(snap->node.header.flags); | ||
442 | ret = lookup_block_ref(root, snap->blocknr, &refs); | ||
443 | BUG_ON(ret); | ||
444 | if (refs == 1 && level != 0) { | ||
445 | struct node *n = &snap->node; | ||
446 | struct tree_buffer *b; | ||
447 | int i; | ||
448 | for (i = 0; i < n->header.nritems; i++) { | ||
449 | b = read_tree_block(root, n->blockptrs[i]); | ||
450 | /* FIXME, don't recurse here */ | ||
451 | ret = btrfs_drop_snapshot(root, b); | ||
452 | BUG_ON(ret); | ||
453 | tree_block_release(root, b); | ||
454 | } | ||
455 | } | ||
456 | ret = free_extent(root, blocknr, 1); | ||
457 | BUG_ON(ret); | ||
458 | return 0; | ||
459 | } | ||
460 | |||