Пример #1
0
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
			     struct btrfs_root *root)
{
	u64 transid = trans->transid;
	int ret = 0;
	struct btrfs_fs_info *fs_info = root->fs_info;

	if (root->commit_root == root->node)
		goto commit_tree;

	free_extent_buffer(root->commit_root);
	root->commit_root = NULL;

	btrfs_set_root_bytenr(&root->root_item, root->node->start);
	btrfs_set_root_generation(&root->root_item, trans->transid);
	root->root_item.level = btrfs_header_level(root->node);
	ret = btrfs_update_root(trans, root->fs_info->tree_root,
				&root->root_key, &root->root_item);
	BUG_ON(ret);
commit_tree:
	ret = commit_tree_roots(trans, fs_info);
	BUG_ON(ret);
	ret = __commit_transaction(trans, root);
	BUG_ON(ret);
	write_ctree_super(trans, root);
	btrfs_finish_extent_commit(trans, fs_info->extent_root,
			           &fs_info->pinned_extents);
	btrfs_free_transaction(root, trans);
	free_extent_buffer(root->commit_root);
	root->commit_root = NULL;
	fs_info->running_transaction = NULL;
	fs_info->last_trans_committed = transid;
	return 0;
}
Пример #2
0
/*
 * this is used to update the root pointer in the tree of tree roots.
 *
 * But, in the case of the extent allocation tree, updating the root
 * pointer may allocate blocks which may change the root of the extent
 * allocation tree.
 *
 * So, this loops and repeats and makes sure the cowonly root didn't
 * change while the root pointer was being updated in the metadata.
 */
static int update_cowonly_root(struct btrfs_trans_handle *trans,
			       struct btrfs_root *root)
{
	int ret;
	u64 old_root_bytenr;
	struct btrfs_root *tree_root = root->fs_info->tree_root;

	btrfs_write_dirty_block_groups(trans, root);

	while (1) {
		old_root_bytenr = btrfs_root_bytenr(&root->root_item);
		if (old_root_bytenr == root->node->start)
			break;

		btrfs_set_root_node(&root->root_item, root->node);
		ret = btrfs_update_root(trans, tree_root,
					&root->root_key,
					&root->root_item);
		BUG_ON(ret);

		ret = btrfs_write_dirty_block_groups(trans, root);
		BUG_ON(ret);
	}

	if (root != root->fs_info->extent_root)
		switch_commit_root(root);

	return 0;
}
Пример #3
0
static int update_cowonly_root(struct btrfs_trans_handle *trans,
			       struct btrfs_root *root)
{
	int ret;
	u64 old_root_bytenr;
	struct btrfs_root *tree_root = root->fs_info->tree_root;

	btrfs_write_dirty_block_groups(trans, root);
	while(1) {
		old_root_bytenr = btrfs_root_bytenr(&root->root_item);
		if (old_root_bytenr == root->node->start)
			break;
		btrfs_set_root_bytenr(&root->root_item,
				       root->node->start);
		btrfs_set_root_generation(&root->root_item,
					  trans->transid);
		root->root_item.level = btrfs_header_level(root->node);
		ret = btrfs_update_root(trans, tree_root,
					&root->root_key,
					&root->root_item);
		BUG_ON(ret);
		btrfs_write_dirty_block_groups(trans, root);
	}
	return 0;
}
Пример #4
0
/*
 * 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;
}
Пример #5
0
static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
                                    struct btrfs_root *root)
{
    struct btrfs_root *gang[8];
    struct btrfs_fs_info *fs_info = root->fs_info;
    int i;
    int ret;
    int err = 0;

    while (1) {
        ret = radix_tree_gang_lookup_tag(&fs_info->fs_roots_radix,
                                         (void **)gang, 0,
                                         ARRAY_SIZE(gang),
                                         BTRFS_ROOT_TRANS_TAG);
        if (ret == 0)
            break;
        for (i = 0; i < ret; i++) {
            root = gang[i];
            radix_tree_tag_clear(&fs_info->fs_roots_radix,
                                 (unsigned long)root->root_key.objectid,
                                 BTRFS_ROOT_TRANS_TAG);

            btrfs_free_log(trans, root);
            btrfs_update_reloc_root(trans, root);
            btrfs_orphan_commit_root(trans, root);

            if (root->commit_root != root->node) {
                switch_commit_root(root);
                btrfs_set_root_node(&root->root_item,
                                    root->node);
            }

            err = btrfs_update_root(trans, fs_info->tree_root,
                                    &root->root_key,
                                    &root->root_item);
            if (err)
                break;
        }
    }
    return err;
}
Пример #6
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();
    }
Пример #7
0
/*
 * at transaction commit time we need to schedule the old roots for
 * deletion via btrfs_drop_snapshot.  This runs through all the
 * reference counted roots that were modified in the current
 * transaction and puts them into the drop list
 */
static noinline int add_dirty_roots(struct btrfs_trans_handle *trans,
                                    struct radix_tree_root *radix,
                                    struct list_head *list)
{
    struct btrfs_dirty_root *dirty;
    struct btrfs_root *gang[8];
    struct btrfs_root *root;
    int i;
    int ret;
    int err = 0;
    u32 refs;

    while (1) {
        ret = radix_tree_gang_lookup_tag(radix, (void **)gang, 0,
                                         ARRAY_SIZE(gang),
                                         BTRFS_ROOT_TRANS_TAG);
        if (ret == 0)
            break;
        for (i = 0; i < ret; i++) {
            root = gang[i];
            radix_tree_tag_clear(radix,
                                 (unsigned long)root->root_key.objectid,
                                 BTRFS_ROOT_TRANS_TAG);

            BUG_ON(!root->ref_tree);
            dirty = root->dirty_root;

            btrfs_free_log(trans, root);
            btrfs_free_reloc_root(trans, root);

            if (root->commit_root == root->node) {
                WARN_ON(root->node->start !=
                        btrfs_root_bytenr(&root->root_item));

                free_extent_buffer(root->commit_root);
                root->commit_root = NULL;
                root->dirty_root = NULL;

                spin_lock(&root->list_lock);
                list_del_init(&dirty->root->dead_list);
                spin_unlock(&root->list_lock);

                kfree(dirty->root);
                kfree(dirty);

                /* make sure to update the root on disk
                 * so we get any updates to the block used
                 * counts
                 */
                err = btrfs_update_root(trans,
                                        root->fs_info->tree_root,
                                        &root->root_key,
                                        &root->root_item);
                continue;
            }

            memset(&root->root_item.drop_progress, 0,
                   sizeof(struct btrfs_disk_key));
            root->root_item.drop_level = 0;
            root->commit_root = NULL;
            root->dirty_root = NULL;
            root->root_key.offset = root->fs_info->generation;
            btrfs_set_root_bytenr(&root->root_item,
                                  root->node->start);
            btrfs_set_root_level(&root->root_item,
                                 btrfs_header_level(root->node));
            btrfs_set_root_generation(&root->root_item,
                                      root->root_key.offset);

            err = btrfs_insert_root(trans, root->fs_info->tree_root,
                                    &root->root_key,
                                    &root->root_item);
            if (err)
                break;

            refs = btrfs_root_refs(&dirty->root->root_item);
            btrfs_set_root_refs(&dirty->root->root_item, refs - 1);
            err = btrfs_update_root(trans, root->fs_info->tree_root,
                                    &dirty->root->root_key,
                                    &dirty->root->root_item);

            BUG_ON(err);
            if (refs == 1) {
                list_add(&dirty->list, list);
            } else {
                WARN_ON(1);
                free_extent_buffer(dirty->root->node);
                kfree(dirty->root);
                kfree(dirty);
            }
        }
    }
    return err;
}
Пример #8
0
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
			     struct btrfs_root *root)
{
	u64 transid = trans->transid;
	int ret = 0;
	struct btrfs_fs_info *fs_info = root->fs_info;

	if (trans->fs_info->transaction_aborted)
		return -EROFS;
	/*
	 * Flush all accumulated delayed refs so that root-tree updates are
	 * consistent
	 */
	ret = btrfs_run_delayed_refs(trans, -1);
	if (ret < 0)
		goto error;

	if (root->commit_root == root->node)
		goto commit_tree;
	if (root == root->fs_info->tree_root)
		goto commit_tree;
	if (root == root->fs_info->chunk_root)
		goto commit_tree;

	free_extent_buffer(root->commit_root);
	root->commit_root = NULL;

	btrfs_set_root_bytenr(&root->root_item, root->node->start);
	btrfs_set_root_generation(&root->root_item, trans->transid);
	root->root_item.level = btrfs_header_level(root->node);
	ret = btrfs_update_root(trans, root->fs_info->tree_root,
				&root->root_key, &root->root_item);
	if (ret < 0)
		goto error;

commit_tree:
	ret = commit_tree_roots(trans, fs_info);
	if (ret < 0)
		goto error;
	/*
	 * Ensure that all committed roots are properly accounted in the
	 * extent tree
	 */
	ret = btrfs_run_delayed_refs(trans, -1);
	if (ret < 0)
		goto error;
	btrfs_write_dirty_block_groups(trans);
	__commit_transaction(trans, root);
	if (ret < 0)
		goto error;
	ret = write_ctree_super(trans);
	btrfs_finish_extent_commit(trans);
	kfree(trans);
	free_extent_buffer(root->commit_root);
	root->commit_root = NULL;
	fs_info->running_transaction = NULL;
	fs_info->last_trans_committed = transid;
	return ret;
error:
	btrfs_destroy_delayed_refs(trans);
	free(trans);
	return ret;
}