예제 #1
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;
}
예제 #2
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;
}
예제 #3
0
int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
			struct btrfs_root *root, int cache_only)
{
	struct btrfs_path *path = NULL;
	struct btrfs_key key;
	int ret = 0;
	int wret;
	int level;
	int is_extent = 0;
	int next_key_ret = 0;
	u64 last_ret = 0;
	u64 min_trans = 0;

	if (cache_only)
		goto out;

	if (root->fs_info->extent_root == root) {
		/*
		 * there's recursion here right now in the tree locking,
		 * we can't defrag the extent root without deadlock
		 */
		goto out;
	}

	if (root->ref_cows == 0 && !is_extent)
		goto out;

	if (btrfs_test_opt(root, SSD))
		goto out;

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	level = btrfs_header_level(root->node);

	if (level == 0)
		goto out;

	if (root->defrag_progress.objectid == 0) {
		struct extent_buffer *root_node;
		u32 nritems;

		root_node = btrfs_lock_root_node(root);
		btrfs_set_lock_blocking(root_node);
		nritems = btrfs_header_nritems(root_node);
		root->defrag_max.objectid = 0;
		/* from above we know this is not a leaf */
		btrfs_node_key_to_cpu(root_node, &root->defrag_max,
				      nritems - 1);
		btrfs_tree_unlock(root_node);
		free_extent_buffer(root_node);
		memset(&key, 0, sizeof(key));
	} else {
		memcpy(&key, &root->defrag_progress, sizeof(key));
	}

	path->keep_locks = 1;
	if (cache_only)
		min_trans = root->defrag_trans_start;

	ret = btrfs_search_forward(root, &key, NULL, path,
				   cache_only, min_trans);
	if (ret < 0)
		goto out;
	if (ret > 0) {
		ret = 0;
		goto out;
	}
	btrfs_release_path(path);
	wret = btrfs_search_slot(trans, root, &key, path, 0, 1);

	if (wret < 0) {
		ret = wret;
		goto out;
	}
	if (!path->nodes[1]) {
		ret = 0;
		goto out;
	}
	path->slots[1] = btrfs_header_nritems(path->nodes[1]);
	next_key_ret = btrfs_find_next_key(root, path, &key, 1, cache_only,
					   min_trans);
	ret = btrfs_realloc_node(trans, root,
				 path->nodes[1], 0,
				 cache_only, &last_ret,
				 &root->defrag_progress);
	if (ret) {
		WARN_ON(ret == -EAGAIN);
		goto out;
	}
	if (next_key_ret == 0) {
		memcpy(&root->defrag_progress, &key, sizeof(key));
		ret = -EAGAIN;
	}
out:
	if (path)
		btrfs_free_path(path);
	if (ret == -EAGAIN) {
		if (root->defrag_max.objectid > root->defrag_progress.objectid)
			goto done;
		if (root->defrag_max.type > root->defrag_progress.type)
			goto done;
		if (root->defrag_max.offset > root->defrag_progress.offset)
			goto done;
		ret = 0;
	}
done:
	if (ret != -EAGAIN) {
		memset(&root->defrag_progress, 0,
		       sizeof(root->defrag_progress));
		root->defrag_trans_start = trans->transid;
	}
	return ret;
}