/*
 * 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;
}
Beispiel #3
0
/*
 * 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();
    }