/*
 * 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;
}
/*
 * new snapshots need to be created at a very specific time in the
 * transaction commit.  This does the actual creation
 */
static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
				   struct btrfs_fs_info *fs_info,
				   struct btrfs_pending_snapshot *pending)
{
	struct btrfs_key key;
	struct btrfs_root_item *new_root_item;
	struct btrfs_root *tree_root = fs_info->tree_root;
	struct btrfs_root *root = pending->root;
	struct extent_buffer *tmp;
	struct extent_buffer *old;
	int ret;
	u64 objectid;

	new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS);
	if (!new_root_item) {
		ret = -ENOMEM;
		goto fail;
	}
	ret = btrfs_find_free_objectid(trans, tree_root, 0, &objectid);
	if (ret)
		goto fail;

	record_root_in_trans(trans, root);
	btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
	memcpy(new_root_item, &root->root_item, sizeof(*new_root_item));

	key.objectid = objectid;
	/* record when the snapshot was created in key.offset */
	key.offset = trans->transid;
	btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);

	old = btrfs_lock_root_node(root);
	btrfs_cow_block(trans, root, old, NULL, 0, &old);
	btrfs_set_lock_blocking(old);

	btrfs_copy_root(trans, root, old, &tmp, objectid);
	btrfs_tree_unlock(old);
	free_extent_buffer(old);

	btrfs_set_root_node(new_root_item, tmp);
	ret = btrfs_insert_root(trans, root->fs_info->tree_root, &key,
				new_root_item);
	btrfs_tree_unlock(tmp);
	free_extent_buffer(tmp);
	if (ret)
		goto fail;

	key.offset = (u64)-1;
	memcpy(&pending->root_key, &key, sizeof(key));
fail:
	kfree(new_root_item);
	btrfs_unreserve_metadata_space(root, 6);
	return ret;
}
示例#3
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;
}
示例#4
0
static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
        struct btrfs_fs_info *fs_info,
        struct btrfs_pending_snapshot *pending)
{
    struct btrfs_key key;
    struct btrfs_root_item *new_root_item;
    struct btrfs_root *tree_root = fs_info->tree_root;
    struct btrfs_root *root = pending->root;
    struct btrfs_root *parent_root;
    struct inode *parent_inode;
    struct dentry *dentry;
    struct extent_buffer *tmp;
    struct extent_buffer *old;
    int ret;
    int retries = 0;
    u64 to_reserve = 0;
    u64 index = 0;
    u64 objectid;

    new_root_item = kmalloc(sizeof(*new_root_item), GFP_NOFS);
    if (!new_root_item) {
        pending->error = -ENOMEM;
        goto fail;
    }

    ret = btrfs_find_free_objectid(trans, tree_root, 0, &objectid);
    if (ret) {
        pending->error = ret;
        goto fail;
    }

    btrfs_reloc_pre_snapshot(trans, pending, &to_reserve);
    btrfs_orphan_pre_snapshot(trans, pending, &to_reserve);

    if (to_reserve > 0) {
        ret = btrfs_block_rsv_add(trans, root, &pending->block_rsv,
                                  to_reserve, &retries);
        if (ret) {
            pending->error = ret;
            goto fail;
        }
    }

    key.objectid = objectid;
    key.offset = (u64)-1;
    key.type = BTRFS_ROOT_ITEM_KEY;

    trans->block_rsv = &pending->block_rsv;

    dentry = pending->dentry;
    parent_inode = dentry->d_parent->d_inode;
    parent_root = BTRFS_I(parent_inode)->root;
    record_root_in_trans(trans, parent_root);

    /*
     * insert the directory item
     */
    ret = btrfs_set_inode_index(parent_inode, &index);
    BUG_ON(ret);
    ret = btrfs_insert_dir_item(trans, parent_root,
                                dentry->d_name.name, dentry->d_name.len,
                                parent_inode->i_ino, &key,
                                BTRFS_FT_DIR, index);
    BUG_ON(ret);

    btrfs_i_size_write(parent_inode, parent_inode->i_size +
                       dentry->d_name.len * 2);
    ret = btrfs_update_inode(trans, parent_root, parent_inode);
    BUG_ON(ret);

    record_root_in_trans(trans, root);
    btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
    memcpy(new_root_item, &root->root_item, sizeof(*new_root_item));

    old = btrfs_lock_root_node(root);
    btrfs_cow_block(trans, root, old, NULL, 0, &old);
    btrfs_set_lock_blocking(old);

    btrfs_copy_root(trans, root, old, &tmp, objectid);
    btrfs_tree_unlock(old);
    free_extent_buffer(old);

    btrfs_set_root_node(new_root_item, tmp);
    /* record when the snapshot was created in key.offset */
    key.offset = trans->transid;
    ret = btrfs_insert_root(trans, tree_root, &key, new_root_item);
    btrfs_tree_unlock(tmp);
    free_extent_buffer(tmp);
    BUG_ON(ret);

    /*
     * insert root back/forward references
     */
    ret = btrfs_add_root_ref(trans, tree_root, objectid,
                             parent_root->root_key.objectid,
                             parent_inode->i_ino, index,
                             dentry->d_name.name, dentry->d_name.len);
    BUG_ON(ret);

    key.offset = (u64)-1;
    pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key);
    BUG_ON(IS_ERR(pending->snap));

    btrfs_reloc_post_snapshot(trans, pending);
    btrfs_orphan_post_snapshot(trans, pending);
fail:
    kfree(new_root_item);
    btrfs_block_rsv_release(root, &pending->block_rsv, (u64)-1);
    return 0;
}