/* * Given a list of roots that need to be deleted, call btrfs_drop_snapshot on * all of them */ int btrfs_drop_dead_root(struct btrfs_root *root) { struct btrfs_trans_handle *trans; struct btrfs_root *tree_root = root->fs_info->tree_root; unsigned long nr; int ret; while (1) { /* * we don't want to jump in and create a bunch of * delayed refs if the transaction is starting to close */ wait_transaction_pre_flush(tree_root->fs_info); trans = btrfs_start_transaction(tree_root, 1); /* * we've joined a transaction, make sure it isn't * closing right now */ if (trans->transaction->delayed_refs.flushing) { btrfs_end_transaction(trans, tree_root); continue; } ret = btrfs_drop_snapshot(trans, root); if (ret != -EAGAIN) break; ret = btrfs_update_root(trans, tree_root, &root->root_key, &root->root_item); if (ret) break; nr = trans->blocks_used; ret = btrfs_end_transaction(trans, tree_root); BUG_ON(ret); btrfs_btree_balance_dirty(tree_root, nr); cond_resched(); } BUG_ON(ret); ret = btrfs_del_root(trans, tree_root, &root->root_key); BUG_ON(ret); nr = trans->blocks_used; ret = btrfs_end_transaction(trans, tree_root); BUG_ON(ret); free_extent_buffer(root->node); free_extent_buffer(root->commit_root); kfree(root); btrfs_btree_balance_dirty(tree_root, nr); return ret; }
int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info) { struct btrfs_trans_handle *trans; struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *free_space_root = fs_info->free_space_root; int ret; trans = btrfs_start_transaction(tree_root, 0); if (IS_ERR(trans)) return PTR_ERR(trans); btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE); fs_info->free_space_root = NULL; ret = clear_free_space_tree(trans, free_space_root); if (ret) goto abort; ret = btrfs_del_root(trans, tree_root, &free_space_root->root_key); if (ret) goto abort; list_del(&free_space_root->dirty_list); btrfs_tree_lock(free_space_root->node); clean_tree_block(trans, tree_root->fs_info, free_space_root->node); btrfs_tree_unlock(free_space_root->node); btrfs_free_tree_block(trans, free_space_root, free_space_root->node, 0, 1); free_extent_buffer(free_space_root->node); free_extent_buffer(free_space_root->commit_root); kfree(free_space_root); ret = btrfs_commit_transaction(trans, tree_root); if (ret) return ret; return 0; abort: btrfs_abort_transaction(trans, ret); btrfs_end_transaction(trans, tree_root); return ret; }
/* * Given a list of roots that need to be deleted, call btrfs_drop_snapshot on * all of them */ static noinline int drop_dirty_roots(struct btrfs_root *tree_root, struct list_head *list) { struct btrfs_dirty_root *dirty; struct btrfs_trans_handle *trans; unsigned long nr; u64 num_bytes; u64 bytes_used; u64 max_useless; int ret = 0; int err; while (!list_empty(list)) { struct btrfs_root *root; dirty = list_entry(list->prev, struct btrfs_dirty_root, list); list_del_init(&dirty->list); num_bytes = btrfs_root_used(&dirty->root->root_item); root = dirty->latest_root; atomic_inc(&root->fs_info->throttles); while (1) { /* * we don't want to jump in and create a bunch of * delayed refs if the transaction is starting to close */ wait_transaction_pre_flush(tree_root->fs_info); trans = btrfs_start_transaction(tree_root, 1); /* * we've joined a transaction, make sure it isn't * closing right now */ if (trans->transaction->delayed_refs.flushing) { btrfs_end_transaction(trans, tree_root); continue; } mutex_lock(&root->fs_info->drop_mutex); ret = btrfs_drop_snapshot(trans, dirty->root); if (ret != -EAGAIN) break; mutex_unlock(&root->fs_info->drop_mutex); err = btrfs_update_root(trans, tree_root, &dirty->root->root_key, &dirty->root->root_item); if (err) ret = err; nr = trans->blocks_used; ret = btrfs_end_transaction(trans, tree_root); BUG_ON(ret); btrfs_btree_balance_dirty(tree_root, nr); cond_resched(); } BUG_ON(ret); atomic_dec(&root->fs_info->throttles); wake_up(&root->fs_info->transaction_throttle); num_bytes -= btrfs_root_used(&dirty->root->root_item); bytes_used = btrfs_root_used(&root->root_item); if (num_bytes) { mutex_lock(&root->fs_info->trans_mutex); btrfs_record_root_in_trans(root); mutex_unlock(&root->fs_info->trans_mutex); btrfs_set_root_used(&root->root_item, bytes_used - num_bytes); } ret = btrfs_del_root(trans, tree_root, &dirty->root->root_key); if (ret) { BUG(); break; } mutex_unlock(&root->fs_info->drop_mutex); spin_lock(&root->list_lock); list_del_init(&dirty->root->dead_list); if (!list_empty(&root->dead_list)) { struct btrfs_root *oldest; oldest = list_entry(root->dead_list.prev, struct btrfs_root, dead_list); max_useless = oldest->root_key.offset - 1; } else { max_useless = root->root_key.offset - 1; } spin_unlock(&root->list_lock); nr = trans->blocks_used; ret = btrfs_end_transaction(trans, tree_root); BUG_ON(ret); ret = btrfs_remove_leaf_refs(root, max_useless, 0); BUG_ON(ret); free_extent_buffer(dirty->root->node); kfree(dirty->root); kfree(dirty); btrfs_btree_balance_dirty(tree_root, nr); cond_resched(); }