diff options
Diffstat (limited to 'extent-tree.c')
-rw-r--r-- | extent-tree.c | 638 |
1 files changed, 368 insertions, 270 deletions
diff --git a/extent-tree.c b/extent-tree.c index 0643815b..8c9cdeff 100644 --- a/extent-tree.c +++ b/extent-tree.c @@ -29,6 +29,7 @@ #include "crc32c.h" #include "volumes.h" #include "free-space-cache.h" +#include "free-space-tree.h" #include "utils.h" #define PENDING_EXTENT_INSERT 0 @@ -44,18 +45,10 @@ struct pending_extent_op { int level; }; -static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 root_objectid, u64 generation, - u64 flags, struct btrfs_disk_key *key, - int level, struct btrfs_key *ins); static int __free_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid, u64 owner_objectid, u64 owner_offset, int refs_to_drop); -static int finish_current_insert(struct btrfs_trans_handle *trans); -static int del_pending_extents(struct btrfs_trans_handle *trans); static struct btrfs_block_group_cache * btrfs_find_block_group(struct btrfs_root *root, struct btrfs_block_group_cache *hint, u64 search_start, int data, int owner); @@ -1420,19 +1413,10 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, err = ret; out: btrfs_free_path(path); - finish_current_insert(trans); - del_pending_extents(trans); BUG_ON(err); return err; } -int btrfs_extent_post_op(struct btrfs_trans_handle *trans) -{ - finish_current_insert(trans); - del_pending_extents(trans); - return 0; -} - int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 bytenr, u64 offset, int metadata, u64 *refs, u64 *flags) @@ -1604,8 +1588,6 @@ again: btrfs_set_extent_flags(l, item, flags); out: btrfs_free_path(path); - finish_current_insert(trans); - del_pending_extents(trans); return ret; } @@ -1703,7 +1685,6 @@ static int write_one_cache_group(struct btrfs_trans_handle *trans, struct btrfs_block_group_cache *cache) { int ret; - int pending_ret; struct btrfs_root *extent_root = trans->fs_info->extent_root; unsigned long bi; struct extent_buffer *leaf; @@ -1719,18 +1700,13 @@ static int write_one_cache_group(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(leaf); btrfs_release_path(path); fail: - finish_current_insert(trans); - pending_ret = del_pending_extents(trans); if (ret) return ret; - if (pending_ret) - return pending_ret; return 0; } -int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans, - struct btrfs_root *root) +int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans) { struct extent_io_tree *block_group_cache; struct btrfs_block_group_cache *cache; @@ -1741,7 +1717,7 @@ int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans, u64 end; u64 ptr; - block_group_cache = &root->fs_info->block_group_cache; + block_group_cache = &trans->fs_info->block_group_cache; path = btrfs_alloc_path(); if (!path) return -ENOMEM; @@ -1912,12 +1888,10 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans, return 0; } -static int update_block_group(struct btrfs_root *root, - u64 bytenr, u64 num_bytes, int alloc, - int mark_free) +static int update_block_group(struct btrfs_fs_info *info, u64 bytenr, + u64 num_bytes, int alloc, int mark_free) { struct btrfs_block_group_cache *cache; - struct btrfs_fs_info *info = root->fs_info; u64 total = num_bytes; u64 old_val; u64 byte_in_group; @@ -1932,14 +1906,6 @@ static int update_block_group(struct btrfs_root *root, old_val -= num_bytes; btrfs_set_super_bytes_used(info->super_copy, old_val); - /* block accounting for root item */ - old_val = btrfs_root_used(&root->root_item); - if (alloc) - old_val += num_bytes; - else - old_val -= num_bytes; - btrfs_set_root_used(&root->root_item, old_val); - while(total) { cache = btrfs_lookup_block_group(info, bytenr); if (!cache) { @@ -2034,73 +2000,8 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, return 0; } -static int extent_root_pending_ops(struct btrfs_fs_info *info) -{ - u64 start; - u64 end; - int ret; - - ret = find_first_extent_bit(&info->extent_ins, 0, &start, - &end, EXTENT_LOCKED); - if (!ret) { - ret = find_first_extent_bit(&info->pending_del, 0, &start, &end, - EXTENT_LOCKED); - } - return ret == 0; - -} -static int finish_current_insert(struct btrfs_trans_handle *trans) -{ - u64 start; - u64 end; - u64 priv; - struct btrfs_fs_info *info = trans->fs_info; - struct btrfs_root *extent_root = info->extent_root; - struct pending_extent_op *extent_op; - struct btrfs_key key; - int ret; - int skinny_metadata = - btrfs_fs_incompat(extent_root->fs_info, SKINNY_METADATA); - - while(1) { - ret = find_first_extent_bit(&info->extent_ins, 0, &start, - &end, EXTENT_LOCKED); - if (ret) - break; - - ret = get_state_private(&info->extent_ins, start, &priv); - BUG_ON(ret); - extent_op = (struct pending_extent_op *)(unsigned long)priv; - - if (extent_op->type == PENDING_EXTENT_INSERT) { - key.objectid = start; - if (skinny_metadata) { - key.offset = extent_op->level; - key.type = BTRFS_METADATA_ITEM_KEY; - } else { - key.offset = extent_op->num_bytes; - key.type = BTRFS_EXTENT_ITEM_KEY; - } - ret = alloc_reserved_tree_block(trans, extent_root, - extent_root->root_key.objectid, - trans->transid, - extent_op->flags, - &extent_op->key, - extent_op->level, &key); - BUG_ON(ret); - } else { - BUG_ON(1); - } - - clear_extent_bits(&info->extent_ins, start, end, EXTENT_LOCKED); - kfree(extent_op); - } - return 0; -} - -static int pin_down_bytes(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 bytenr, u64 num_bytes, int is_data) +static int pin_down_bytes(struct btrfs_trans_handle *trans, u64 bytenr, + u64 num_bytes, int is_data) { int err = 0; struct extent_buffer *buf; @@ -2108,7 +2009,7 @@ static int pin_down_bytes(struct btrfs_trans_handle *trans, if (is_data) goto pinit; - buf = btrfs_find_tree_block(root->fs_info, bytenr, num_bytes); + buf = btrfs_find_tree_block(trans->fs_info, bytenr, num_bytes); if (!buf) goto pinit; @@ -2152,7 +2053,6 @@ void btrfs_unpin_extent(struct btrfs_fs_info *fs_info, * remove an extent from the root, returns 0 on success */ static int __free_extent(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid, u64 owner_objectid, u64 owner_offset, int refs_to_drop) @@ -2160,7 +2060,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_key key; struct btrfs_path *path; - struct btrfs_root *extent_root = root->fs_info->extent_root; + struct btrfs_root *extent_root = trans->fs_info->extent_root; struct extent_buffer *leaf; struct btrfs_extent_item *ei; struct btrfs_extent_inline_ref *iref; @@ -2174,8 +2074,8 @@ static int __free_extent(struct btrfs_trans_handle *trans, int skinny_metadata = btrfs_fs_incompat(extent_root->fs_info, SKINNY_METADATA); - if (root->fs_info->free_extent_hook) { - root->fs_info->free_extent_hook(trans, root, bytenr, num_bytes, + if (trans->fs_info->free_extent_hook) { + trans->fs_info->free_extent_hook(trans->fs_info, bytenr, num_bytes, parent, root_objectid, owner_objectid, owner_offset, refs_to_drop); @@ -2360,7 +2260,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, } if (pin) { - ret = pin_down_bytes(trans, root, bytenr, num_bytes, + ret = pin_down_bytes(trans, bytenr, num_bytes, is_data); if (ret > 0) mark_free = 1; @@ -2373,79 +2273,23 @@ static int __free_extent(struct btrfs_trans_handle *trans, btrfs_release_path(path); if (is_data) { - ret = btrfs_del_csums(trans, root, bytenr, num_bytes); + ret = btrfs_del_csums(trans, bytenr, num_bytes); BUG_ON(ret); } - update_block_group(root, bytenr, num_bytes, 0, mark_free); + ret = add_to_free_space_tree(trans, bytenr, num_bytes); + if (ret) { + goto fail; + } + + update_block_group(trans->fs_info, bytenr, num_bytes, 0, + mark_free); } fail: btrfs_free_path(path); - finish_current_insert(trans); return ret; } -/* - * find all the blocks marked as pending in the radix tree and remove - * them from the extent map - */ -static int del_pending_extents(struct btrfs_trans_handle *trans) -{ - int ret; - int err = 0; - u64 start; - u64 end; - u64 priv; - struct extent_io_tree *pending_del; - struct extent_io_tree *extent_ins; - struct pending_extent_op *extent_op; - struct btrfs_fs_info *fs_info = trans->fs_info; - struct btrfs_root *extent_root = fs_info->extent_root; - - extent_ins = &extent_root->fs_info->extent_ins; - pending_del = &extent_root->fs_info->pending_del; - - while(1) { - ret = find_first_extent_bit(pending_del, 0, &start, &end, - EXTENT_LOCKED); - if (ret) - break; - - ret = get_state_private(pending_del, start, &priv); - BUG_ON(ret); - extent_op = (struct pending_extent_op *)(unsigned long)priv; - - clear_extent_bits(pending_del, start, end, EXTENT_LOCKED); - - if (!test_range_bit(extent_ins, start, end, - EXTENT_LOCKED, 0)) { - ret = __free_extent(trans, extent_root, - start, end + 1 - start, 0, - extent_root->root_key.objectid, - extent_op->level, 0, 1); - kfree(extent_op); - } else { - kfree(extent_op); - ret = get_state_private(extent_ins, start, &priv); - BUG_ON(ret); - extent_op = (struct pending_extent_op *) - (unsigned long)priv; - - clear_extent_bits(extent_ins, start, end, - EXTENT_LOCKED); - - if (extent_op->type == PENDING_BACKREF_UPDATE) - BUG_ON(1); - - kfree(extent_op); - } - if (ret) - err = ret; - } - return err; -} - - int btrfs_free_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct extent_buffer *buf, @@ -2465,33 +2309,30 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 parent, u64 root_objectid, u64 owner, u64 offset) { - struct btrfs_root *extent_root = root->fs_info->extent_root; - int pending_ret; int ret; WARN_ON(num_bytes < root->fs_info->sectorsize); - if (root == extent_root) { - struct pending_extent_op *extent_op; - - extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS); - BUG_ON(!extent_op); - - extent_op->type = PENDING_EXTENT_DELETE; - extent_op->bytenr = bytenr; - extent_op->num_bytes = num_bytes; - extent_op->level = (int)owner; - - set_extent_bits(&root->fs_info->pending_del, - bytenr, bytenr + num_bytes - 1, - EXTENT_LOCKED); - set_state_private(&root->fs_info->pending_del, - bytenr, (unsigned long)extent_op); - return 0; + /* + * tree log blocks never actually go into the extent allocation + * tree, just update pinning info and exit early. + */ + if (root_objectid == BTRFS_TREE_LOG_OBJECTID) { + printf("PINNING EXTENTS IN LOG TREE\n"); + WARN_ON(owner >= BTRFS_FIRST_FREE_OBJECTID); + btrfs_pin_extent(trans->fs_info, bytenr, num_bytes); + ret = 0; + } else if (owner < BTRFS_FIRST_FREE_OBJECTID) { + BUG_ON(offset); + ret = btrfs_add_delayed_tree_ref(trans->fs_info, trans, + bytenr, num_bytes, parent, + root_objectid, (int)owner, + BTRFS_DROP_DELAYED_REF, + NULL, NULL, NULL); + } else { + ret = __free_extent(trans, bytenr, num_bytes, parent, + root_objectid, owner, offset, 1); } - ret = __free_extent(trans, root, bytenr, num_bytes, parent, - root_objectid, owner, offset, 1); - pending_ret = del_pending_extents(trans); - return ret ? ret : pending_ret; + return ret; } static u64 stripe_align(struct btrfs_root *root, u64 val) @@ -2690,57 +2531,88 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, } static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, - struct btrfs_root *root, - u64 root_objectid, u64 generation, - u64 flags, struct btrfs_disk_key *key, - int level, struct btrfs_key *ins) + struct btrfs_delayed_ref_node *node, + struct btrfs_delayed_extent_op *extent_op) { - int ret; - struct btrfs_fs_info *fs_info = root->fs_info; + + struct btrfs_delayed_tree_ref *ref = btrfs_delayed_node_to_tree_ref(node); + bool skinny_metadata = btrfs_fs_incompat(trans->fs_info, SKINNY_METADATA); + struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_extent_item *extent_item; - struct btrfs_tree_block_info *block_info; struct btrfs_extent_inline_ref *iref; - struct btrfs_path *path; struct extent_buffer *leaf; + struct btrfs_path *path; + struct btrfs_key ins; u32 size = sizeof(*extent_item) + sizeof(*iref); - int skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA); + u64 start, end; + int ret; - if (!skinny_metadata) - size += sizeof(*block_info); + ins.objectid = node->bytenr; + if (skinny_metadata) { + ins.offset = ref->level; + ins.type = BTRFS_METADATA_ITEM_KEY; + } else { + ins.offset = node->num_bytes; + ins.type = BTRFS_EXTENT_ITEM_KEY; + + size += sizeof(struct btrfs_tree_block_info); + } + + if (ref->root == BTRFS_EXTENT_TREE_OBJECTID) { + ret = find_first_extent_bit(&trans->fs_info->extent_ins, + node->bytenr, &start, &end, + EXTENT_LOCKED); + ASSERT(!ret); + ASSERT(start == node->bytenr); + ASSERT(end == node->bytenr + node->num_bytes - 1); + } path = btrfs_alloc_path(); if (!path) return -ENOMEM; ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path, - ins, size); - BUG_ON(ret); + &ins, size); + if (ret) + return ret; leaf = path->nodes[0]; extent_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); btrfs_set_extent_refs(leaf, extent_item, 1); - btrfs_set_extent_generation(leaf, extent_item, generation); + btrfs_set_extent_generation(leaf, extent_item, trans->transid); btrfs_set_extent_flags(leaf, extent_item, - flags | BTRFS_EXTENT_FLAG_TREE_BLOCK); + extent_op->flags_to_set | + BTRFS_EXTENT_FLAG_TREE_BLOCK); if (skinny_metadata) { iref = (struct btrfs_extent_inline_ref *)(extent_item + 1); } else { + struct btrfs_tree_block_info *block_info; block_info = (struct btrfs_tree_block_info *)(extent_item + 1); - btrfs_set_tree_block_key(leaf, block_info, key); - btrfs_set_tree_block_level(leaf, block_info, level); + btrfs_set_tree_block_key(leaf, block_info, &extent_op->key); + btrfs_set_tree_block_level(leaf, block_info, ref->level); iref = (struct btrfs_extent_inline_ref *)(block_info + 1); } btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY); - btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid); + btrfs_set_extent_inline_ref_offset(leaf, iref, ref->root); btrfs_mark_buffer_dirty(leaf); btrfs_free_path(path); - ret = update_block_group(root, ins->objectid, fs_info->nodesize, - 1, 0); + ret = remove_from_free_space_tree(trans, ins.objectid, fs_info->nodesize); + if (ret) + return ret; + + ret = update_block_group(fs_info, ins.objectid, fs_info->nodesize, 1, + 0); + + if (ref->root == BTRFS_EXTENT_TREE_OBJECTID) { + clear_extent_bits(&trans->fs_info->extent_ins, start, end, + EXTENT_LOCKED); + } + return ret; } @@ -2752,39 +2624,51 @@ static int alloc_tree_block(struct btrfs_trans_handle *trans, u64 search_end, struct btrfs_key *ins) { int ret; + u64 extent_size; + struct btrfs_delayed_extent_op *extent_op; + bool skinny_metadata = btrfs_fs_incompat(root->fs_info, + SKINNY_METADATA); + + extent_op = btrfs_alloc_delayed_extent_op(); + if (!extent_op) + return -ENOMEM; + ret = btrfs_reserve_extent(trans, root, num_bytes, empty_size, hint_byte, search_end, ins, 0); - BUG_ON(ret); + if (ret < 0) + return ret; + if (key) + memcpy(&extent_op->key, key, sizeof(extent_op->key)); + else + memset(&extent_op->key, 0, sizeof(extent_op->key)); + extent_op->flags_to_set = flags; + extent_op->update_key = skinny_metadata ? false : true; + extent_op->update_flags = true; + extent_op->is_data = false; + extent_op->level = level; + + extent_size = ins->offset; + + if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) { + ins->offset = level; + ins->type = BTRFS_METADATA_ITEM_KEY; + } + + /* Ensure this reserved extent is not found by the allocator */ if (root_objectid == BTRFS_EXTENT_TREE_OBJECTID) { - struct pending_extent_op *extent_op; - - extent_op = kmalloc(sizeof(*extent_op), GFP_NOFS); - BUG_ON(!extent_op); - - extent_op->type = PENDING_EXTENT_INSERT; - extent_op->bytenr = ins->objectid; - extent_op->num_bytes = ins->offset; - extent_op->level = level; - extent_op->flags = flags; - memcpy(&extent_op->key, key, sizeof(*key)); - - set_extent_bits(&root->fs_info->extent_ins, ins->objectid, - ins->objectid + ins->offset - 1, - EXTENT_LOCKED); - set_state_private(&root->fs_info->extent_ins, - ins->objectid, (unsigned long)extent_op); - } else { - if (btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) { - ins->offset = level; - ins->type = BTRFS_METADATA_ITEM_KEY; - } - ret = alloc_reserved_tree_block(trans, root, root_objectid, - generation, flags, - key, level, ins); - finish_current_insert(trans); - del_pending_extents(trans); + ret = set_extent_bits(&trans->fs_info->extent_ins, + ins->objectid, + ins->objectid + extent_size - 1, + EXTENT_LOCKED); + + BUG_ON(ret); } + + ret = btrfs_add_delayed_tree_ref(root->fs_info, trans, ins->objectid, + extent_size, 0, root_objectid, + level, BTRFS_ADD_DELAYED_EXTENT, + extent_op, NULL, NULL); return ret; } @@ -2975,7 +2859,7 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans, path->slots[*level]++; ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent->start, root_owner, - root_gen, *level - 1, 1); + root_gen, *level - 1, 0); BUG_ON(ret); continue; } @@ -3017,7 +2901,7 @@ out: root_gen = btrfs_header_generation(parent); ret = btrfs_free_extent(trans, root, path->nodes[*level]->start, path->nodes[*level]->len, parent->start, - root_owner, root_gen, *level, 1); + root_owner, root_gen, *level, 0); free_extent_buffer(path->nodes[*level]); path->nodes[*level] = NULL; *level += 1; @@ -3068,7 +2952,7 @@ static int noinline walk_up_tree(struct btrfs_trans_handle *trans, path->nodes[*level]->start, path->nodes[*level]->len, parent->start, root_owner, - root_gen, *level, 1); + root_gen, *level, 0); BUG_ON(ret); free_extent_buffer(path->nodes[*level]); path->nodes[*level] = NULL; @@ -3309,11 +3193,6 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans, sizeof(cache->item)); BUG_ON(ret); - ret = finish_current_insert(trans); - BUG_ON(ret); - ret = del_pending_extents(trans); - BUG_ON(ret); - return 0; } @@ -3409,10 +3288,6 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans, sizeof(cache->item)); BUG_ON(ret); - finish_current_insert(trans); - ret = del_pending_extents(trans); - BUG_ON(ret); - cur_start = cache->key.objectid + cache->key.offset; } return 0; @@ -3422,7 +3297,7 @@ int btrfs_update_block_group(struct btrfs_root *root, u64 bytenr, u64 num_bytes, int alloc, int mark_free) { - return update_block_group(root, bytenr, num_bytes, + return update_block_group(root->fs_info, bytenr, num_bytes, alloc, mark_free); } @@ -3794,14 +3669,9 @@ int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans) struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_root *root = fs_info->extent_root; - while(extent_root_pending_ops(fs_info)) { - ret = finish_current_insert(trans); - if (ret) - return ret; - ret = del_pending_extents(trans); - if (ret) - return ret; - } + ret = btrfs_run_delayed_refs(trans, -1); + if (ret) + return ret; while(1) { cache = btrfs_lookup_first_block_group(fs_info, start); @@ -3879,7 +3749,7 @@ static void __get_extent_size(struct btrfs_root *root, struct btrfs_path *path, * Return >0 for not found. * Return <0 for err */ -int btrfs_search_overlap_extent(struct btrfs_root *root, +static int btrfs_search_overlap_extent(struct btrfs_root *root, struct btrfs_path *path, u64 bytenr, u64 len) { struct btrfs_key key; @@ -4006,7 +3876,7 @@ static int __btrfs_record_file_extent(struct btrfs_trans_handle *trans, } else if (ret != -EEXIST) { goto fail; } - btrfs_extent_post_op(trans); + btrfs_run_delayed_refs(trans, -1); extent_bytenr = disk_bytenr; extent_num_bytes = num_bytes; extent_offset = 0; @@ -4197,3 +4067,231 @@ u64 add_new_free_space(struct btrfs_block_group_cache *block_group, return total_added; } + +static void cleanup_extent_op(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, + struct btrfs_delayed_ref_head *head) +{ + struct btrfs_delayed_extent_op *extent_op = head->extent_op; + + if (!extent_op) + return; + head->extent_op = NULL; + btrfs_free_delayed_extent_op(extent_op); +} + +static void unselect_delayed_ref_head(struct btrfs_delayed_ref_root *delayed_refs, + struct btrfs_delayed_ref_head *head) +{ + head->processing = 0; + delayed_refs->num_heads_ready++; +} + +static int cleanup_ref_head(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, + struct btrfs_delayed_ref_head *head) +{ + struct btrfs_delayed_ref_root *delayed_refs; + + delayed_refs = &trans->delayed_refs; + + cleanup_extent_op(trans, fs_info, head); + + /* + * Need to drop our head ref lock and re-acquire the delayed ref lock + * and then re-check to make sure nobody got added. + */ + if (!RB_EMPTY_ROOT(&head->ref_tree) || head->extent_op) + return 1; + + delayed_refs->num_heads--; + rb_erase(&head->href_node, &delayed_refs->href_root); + RB_CLEAR_NODE(&head->href_node); + + if (head->must_insert_reserved) + btrfs_pin_extent(fs_info, head->bytenr, head->num_bytes); + + btrfs_put_delayed_ref_head(head); + return 0; +} + +static inline struct btrfs_delayed_ref_node * +select_delayed_ref(struct btrfs_delayed_ref_head *head) +{ + struct btrfs_delayed_ref_node *ref; + + if (RB_EMPTY_ROOT(&head->ref_tree)) + return NULL; + /* + * Select a delayed ref of type BTRFS_ADD_DELAYED_REF first. + * This is to prevent a ref count from going down to zero, which deletes + * the extent item from the extent tree, when there still are references + * to add, which would fail because they would not find the extent item. + */ + if (!list_empty(&head->ref_add_list)) + return list_first_entry(&head->ref_add_list, + struct btrfs_delayed_ref_node, + add_list); + ref = rb_entry(rb_first(&head->ref_tree), + struct btrfs_delayed_ref_node, ref_node); + ASSERT(list_empty(&ref->add_list)); + return ref; +} + + +static int run_delayed_tree_ref(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, + struct btrfs_delayed_ref_node *node, + struct btrfs_delayed_extent_op *extent_op, + int insert_reserved) +{ + int ret = 0; + struct btrfs_delayed_tree_ref *ref; + u64 parent = 0; + u64 ref_root = 0; + + ref = btrfs_delayed_node_to_tree_ref(node); + + if (node->type == BTRFS_SHARED_BLOCK_REF_KEY) + parent = ref->parent; + ref_root = ref->root; + + if (node->ref_mod != 1) { + printf("btree block(%llu) has %d references rather than 1: action %d ref_root %llu parent %llu", + node->bytenr, node->ref_mod, node->action, ref_root, + parent); + return -EIO; + } + if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) { + BUG_ON(!extent_op || !extent_op->update_flags); + ret = alloc_reserved_tree_block(trans, node, extent_op); + } else if (node->action == BTRFS_DROP_DELAYED_REF) { + struct btrfs_delayed_tree_ref *ref = btrfs_delayed_node_to_tree_ref(node); + ret = __free_extent(trans, node->bytenr, node->num_bytes, + ref->parent, ref->root, ref->level, 0, 1); + } else { + BUG(); + } + + return ret; +} + +/* helper function to actually process a single delayed ref entry */ +static int run_one_delayed_ref(struct btrfs_trans_handle *trans, + struct btrfs_fs_info *fs_info, + struct btrfs_delayed_ref_node *node, + struct btrfs_delayed_extent_op *extent_op, + int insert_reserved) +{ + int ret = 0; + + if (node->type == BTRFS_TREE_BLOCK_REF_KEY || + node->type == BTRFS_SHARED_BLOCK_REF_KEY) { + ret = run_delayed_tree_ref(trans, fs_info, node, extent_op, + insert_reserved); + } else + BUG(); + return ret; +} + +int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, unsigned long nr) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + struct btrfs_delayed_ref_root *delayed_refs; + struct btrfs_delayed_ref_node *ref; + struct btrfs_delayed_ref_head *locked_ref = NULL; + struct btrfs_delayed_extent_op *extent_op; + int ret; + int must_insert_reserved = 0; + + delayed_refs = &trans->delayed_refs; + while (1) { + if (!locked_ref) { + locked_ref = btrfs_select_ref_head(trans); + if (!locked_ref) + break; + } + /* + * We need to try and merge add/drops of the same ref since we + * can run into issues with relocate dropping the implicit ref + * and then it being added back again before the drop can + * finish. If we merged anything we need to re-loop so we can + * get a good ref. + * Or we can get node references of the same type that weren't + * merged when created due to bumps in the tree mod seq, and + * we need to merge them to prevent adding an inline extent + * backref before dropping it (triggering a BUG_ON at + * insert_inline_extent_backref()). + */ + btrfs_merge_delayed_refs(trans, delayed_refs, locked_ref); + ref = select_delayed_ref(locked_ref); + /* + * We're done processing refs in this ref_head, clean everything + * up and move on to the next ref_head. + */ + if (!ref) { + ret = cleanup_ref_head(trans, fs_info, locked_ref); + if (ret > 0 ) { + /* We dropped our lock, we need to loop. */ + ret = 0; + continue; + } else if (ret) { + return ret; + } + locked_ref = NULL; + continue; + } + + ref->in_tree = 0; + rb_erase(&ref->ref_node, &locked_ref->ref_tree); + RB_CLEAR_NODE(&ref->ref_node); + if (!list_empty(&ref->add_list)) + list_del(&ref->add_list); + /* + * When we play the delayed ref, also correct the ref_mod on + * head + */ + switch (ref->action) { + case BTRFS_ADD_DELAYED_REF: + case BTRFS_ADD_DELAYED_EXTENT: + locked_ref->ref_mod -= ref->ref_mod; + break; + case BTRFS_DROP_DELAYED_REF: + locked_ref->ref_mod += ref->ref_mod; + break; + default: + WARN_ON(1); + } + + /* + * Record the must-insert_reserved flag before we drop the spin + * lock. + */ + must_insert_reserved = locked_ref->must_insert_reserved; + locked_ref->must_insert_reserved = 0; + + extent_op = locked_ref->extent_op; + locked_ref->extent_op = NULL; + + ret = run_one_delayed_ref(trans, fs_info, ref, extent_op, + must_insert_reserved); + + btrfs_free_delayed_extent_op(extent_op); + /* + * If we are re-initing extent tree in this transaction + * failure in freeing old roots are expected (because we don't + * have the old extent tree, hence backref resolution will + * return -EIO). + */ + if (ret && (!trans->reinit_extent_tree || + ref->action != BTRFS_DROP_DELAYED_REF)) { + unselect_delayed_ref_head(delayed_refs, locked_ref); + btrfs_put_delayed_ref(ref); + return ret; + } + + btrfs_put_delayed_ref(ref); + } + + return 0; +} |