diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-03-27 06:33:00 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-03-27 06:33:00 -0400 |
commit | 6407bf6d7c449cbfb0a39d985194e265eda3baf4 (patch) | |
tree | 29b7226d36b1fbbe2a6bed134ddc3f8d98853bb2 /fs/btrfs/extent-tree.c | |
parent | dee26a9f7aab7ffe1193cd1415b23a69426acc9f (diff) |
Btrfs: reference counts on data extents
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 | 87 |
1 files changed, 70 insertions, 17 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 82f6e9eed1d0..4d4fc48c0a31 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -13,7 +13,7 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct | |||
13 | btrfs_root *extent_root); | 13 | btrfs_root *extent_root); |
14 | 14 | ||
15 | static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root | 15 | static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root |
16 | *root, u64 blocknr) | 16 | *root, u64 blocknr, u64 num_blocks) |
17 | { | 17 | { |
18 | struct btrfs_path path; | 18 | struct btrfs_path path; |
19 | int ret; | 19 | int ret; |
@@ -29,7 +29,7 @@ static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root | |||
29 | key.objectid = blocknr; | 29 | key.objectid = blocknr; |
30 | key.flags = 0; | 30 | key.flags = 0; |
31 | btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); | 31 | btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); |
32 | key.offset = 1; | 32 | key.offset = num_blocks; |
33 | ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path, | 33 | ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path, |
34 | 0, 1); | 34 | 0, 1); |
35 | if (ret != 0) | 35 | if (ret != 0) |
@@ -48,7 +48,7 @@ static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root | |||
48 | } | 48 | } |
49 | 49 | ||
50 | static int lookup_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root | 50 | static int lookup_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root |
51 | *root, u64 blocknr, u32 *refs) | 51 | *root, u64 blocknr, u64 num_blocks, u32 *refs) |
52 | { | 52 | { |
53 | struct btrfs_path path; | 53 | struct btrfs_path path; |
54 | int ret; | 54 | int ret; |
@@ -57,7 +57,7 @@ static int lookup_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root | |||
57 | struct btrfs_extent_item *item; | 57 | struct btrfs_extent_item *item; |
58 | btrfs_init_path(&path); | 58 | btrfs_init_path(&path); |
59 | key.objectid = blocknr; | 59 | key.objectid = blocknr; |
60 | key.offset = 1; | 60 | key.offset = num_blocks; |
61 | key.flags = 0; | 61 | key.flags = 0; |
62 | btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); | 62 | btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY); |
63 | ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path, | 63 | ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, &path, |
@@ -76,17 +76,34 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
76 | { | 76 | { |
77 | u64 blocknr; | 77 | u64 blocknr; |
78 | struct btrfs_node *buf_node; | 78 | struct btrfs_node *buf_node; |
79 | struct btrfs_leaf *buf_leaf; | ||
80 | struct btrfs_disk_key *key; | ||
81 | struct btrfs_file_extent_item *fi; | ||
79 | int i; | 82 | int i; |
83 | int leaf; | ||
84 | int ret; | ||
80 | 85 | ||
81 | if (!root->ref_cows) | 86 | if (!root->ref_cows) |
82 | return 0; | 87 | return 0; |
83 | buf_node = btrfs_buffer_node(buf); | 88 | buf_node = btrfs_buffer_node(buf); |
84 | if (btrfs_is_leaf(buf_node)) | 89 | leaf = btrfs_is_leaf(buf_node); |
85 | return 0; | 90 | buf_leaf = btrfs_buffer_leaf(buf); |
86 | |||
87 | for (i = 0; i < btrfs_header_nritems(&buf_node->header); i++) { | 91 | for (i = 0; i < btrfs_header_nritems(&buf_node->header); i++) { |
88 | blocknr = btrfs_node_blockptr(buf_node, i); | 92 | if (leaf) { |
89 | inc_block_ref(trans, root, blocknr); | 93 | key = &buf_leaf->items[i].key; |
94 | if (btrfs_disk_key_type(key) != BTRFS_EXTENT_DATA_KEY) | ||
95 | continue; | ||
96 | fi = btrfs_item_ptr(buf_leaf, i, | ||
97 | struct btrfs_file_extent_item); | ||
98 | ret = inc_block_ref(trans, root, | ||
99 | btrfs_file_extent_disk_blocknr(fi), | ||
100 | btrfs_file_extent_disk_num_blocks(fi)); | ||
101 | BUG_ON(ret); | ||
102 | } else { | ||
103 | blocknr = btrfs_node_blockptr(buf_node, i); | ||
104 | ret = inc_block_ref(trans, root, blocknr, 1); | ||
105 | BUG_ON(ret); | ||
106 | } | ||
90 | } | 107 | } |
91 | return 0; | 108 | return 0; |
92 | } | 109 | } |
@@ -469,6 +486,37 @@ struct buffer_head *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, | |||
469 | return buf; | 486 | return buf; |
470 | } | 487 | } |
471 | 488 | ||
489 | static int drop_leaf_ref(struct btrfs_trans_handle *trans, | ||
490 | struct btrfs_root *root, struct buffer_head *cur) | ||
491 | { | ||
492 | struct btrfs_disk_key *key; | ||
493 | struct btrfs_leaf *leaf; | ||
494 | struct btrfs_file_extent_item *fi; | ||
495 | int i; | ||
496 | int nritems; | ||
497 | int ret; | ||
498 | |||
499 | BUG_ON(!btrfs_is_leaf(btrfs_buffer_node(cur))); | ||
500 | leaf = btrfs_buffer_leaf(cur); | ||
501 | nritems = btrfs_header_nritems(&leaf->header); | ||
502 | for (i = 0; i < nritems; i++) { | ||
503 | key = &leaf->items[i].key; | ||
504 | if (btrfs_disk_key_type(key) != BTRFS_EXTENT_DATA_KEY) | ||
505 | continue; | ||
506 | fi = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item); | ||
507 | /* | ||
508 | * FIXME make sure to insert a trans record that | ||
509 | * repeats the snapshot del on crash | ||
510 | */ | ||
511 | ret = btrfs_free_extent(trans, root, | ||
512 | btrfs_file_extent_disk_blocknr(fi), | ||
513 | btrfs_file_extent_disk_num_blocks(fi), | ||
514 | 0); | ||
515 | BUG_ON(ret); | ||
516 | } | ||
517 | return 0; | ||
518 | } | ||
519 | |||
472 | /* | 520 | /* |
473 | * helper function for drop_snapshot, this walks down the tree dropping ref | 521 | * helper function for drop_snapshot, this walks down the tree dropping ref |
474 | * counts as it goes. | 522 | * counts as it goes. |
@@ -483,28 +531,33 @@ static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root | |||
483 | u32 refs; | 531 | u32 refs; |
484 | 532 | ||
485 | ret = lookup_block_ref(trans, root, path->nodes[*level]->b_blocknr, | 533 | ret = lookup_block_ref(trans, root, path->nodes[*level]->b_blocknr, |
486 | &refs); | 534 | 1, &refs); |
487 | BUG_ON(ret); | 535 | BUG_ON(ret); |
488 | if (refs > 1) | 536 | if (refs > 1) |
489 | goto out; | 537 | goto out; |
490 | /* | 538 | /* |
491 | * walk down to the last node level and free all the leaves | 539 | * walk down to the last node level and free all the leaves |
492 | */ | 540 | */ |
493 | while(*level > 0) { | 541 | while(*level >= 0) { |
494 | cur = path->nodes[*level]; | 542 | cur = path->nodes[*level]; |
495 | if (path->slots[*level] >= | 543 | if (path->slots[*level] >= |
496 | btrfs_header_nritems(btrfs_buffer_header(cur))) | 544 | btrfs_header_nritems(btrfs_buffer_header(cur))) |
497 | break; | 545 | break; |
546 | if (*level == 0) { | ||
547 | ret = drop_leaf_ref(trans, root, cur); | ||
548 | BUG_ON(ret); | ||
549 | break; | ||
550 | } | ||
498 | blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur), | 551 | blocknr = btrfs_node_blockptr(btrfs_buffer_node(cur), |
499 | path->slots[*level]); | 552 | path->slots[*level]); |
500 | ret = lookup_block_ref(trans, root, blocknr, &refs); | 553 | ret = lookup_block_ref(trans, root, blocknr, 1, &refs); |
501 | if (refs != 1 || *level == 1) { | 554 | BUG_ON(ret); |
555 | if (refs != 1) { | ||
502 | path->slots[*level]++; | 556 | path->slots[*level]++; |
503 | ret = btrfs_free_extent(trans, root, blocknr, 1, 1); | 557 | ret = btrfs_free_extent(trans, root, blocknr, 1, 1); |
504 | BUG_ON(ret); | 558 | BUG_ON(ret); |
505 | continue; | 559 | continue; |
506 | } | 560 | } |
507 | BUG_ON(ret); | ||
508 | next = read_tree_block(root, blocknr); | 561 | next = read_tree_block(root, blocknr); |
509 | if (path->nodes[*level-1]) | 562 | if (path->nodes[*level-1]) |
510 | btrfs_block_release(root, path->nodes[*level-1]); | 563 | btrfs_block_release(root, path->nodes[*level-1]); |
@@ -513,8 +566,8 @@ static int walk_down_tree(struct btrfs_trans_handle *trans, struct btrfs_root | |||
513 | path->slots[*level] = 0; | 566 | path->slots[*level] = 0; |
514 | } | 567 | } |
515 | out: | 568 | out: |
516 | ret = btrfs_free_extent(trans, root, path->nodes[*level]->b_blocknr, | 569 | ret = btrfs_free_extent(trans, root, |
517 | 1, 1); | 570 | path->nodes[*level]->b_blocknr, 1, 1); |
518 | btrfs_block_release(root, path->nodes[*level]); | 571 | btrfs_block_release(root, path->nodes[*level]); |
519 | path->nodes[*level] = NULL; | 572 | path->nodes[*level] = NULL; |
520 | *level += 1; | 573 | *level += 1; |
@@ -544,10 +597,10 @@ static int walk_up_tree(struct btrfs_trans_handle *trans, struct btrfs_root | |||
544 | ret = btrfs_free_extent(trans, root, | 597 | ret = btrfs_free_extent(trans, root, |
545 | path->nodes[*level]->b_blocknr, | 598 | path->nodes[*level]->b_blocknr, |
546 | 1, 1); | 599 | 1, 1); |
600 | BUG_ON(ret); | ||
547 | btrfs_block_release(root, path->nodes[*level]); | 601 | btrfs_block_release(root, path->nodes[*level]); |
548 | path->nodes[*level] = NULL; | 602 | path->nodes[*level] = NULL; |
549 | *level = i + 1; | 603 | *level = i + 1; |
550 | BUG_ON(ret); | ||
551 | } | 604 | } |
552 | } | 605 | } |
553 | return 1; | 606 | return 1; |