/* * 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; }
/* * 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(); }