コード例 #1
0
int add_block_group_free_space(struct btrfs_trans_handle *trans,
                               struct btrfs_fs_info *fs_info,
                               struct btrfs_block_group_cache *block_group)
{
    struct btrfs_path *path = NULL;
    int ret = 0;

    if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE))
        return 0;

    mutex_lock(&block_group->free_space_lock);
    if (!block_group->needs_free_space)
        goto out;

    path = btrfs_alloc_path();
    if (!path) {
        ret = -ENOMEM;
        goto out;
    }

    ret = __add_block_group_free_space(trans, fs_info, block_group, path);

out:
    btrfs_free_path(path);
    mutex_unlock(&block_group->free_space_lock);
    if (ret)
        btrfs_abort_transaction(trans, ret);
    return ret;
}
コード例 #2
0
ファイル: free-space-tree.c プロジェクト: AlexShiLucky/linux
int remove_from_free_space_tree(struct btrfs_trans_handle *trans,
				u64 start, u64 size)
{
	struct btrfs_block_group_cache *block_group;
	struct btrfs_path *path;
	int ret;

	if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE))
		return 0;

	path = btrfs_alloc_path();
	if (!path) {
		ret = -ENOMEM;
		goto out;
	}

	block_group = btrfs_lookup_block_group(trans->fs_info, start);
	if (!block_group) {
		ASSERT(0);
		ret = -ENOENT;
		goto out;
	}

	mutex_lock(&block_group->free_space_lock);
	ret = __remove_from_free_space_tree(trans, block_group, path, start,
					    size);
	mutex_unlock(&block_group->free_space_lock);

	btrfs_put_block_group(block_group);
out:
	btrfs_free_path(path);
	if (ret)
		btrfs_abort_transaction(trans, ret);
	return ret;
}
コード例 #3
0
int btrfs_create_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;
    struct btrfs_block_group_cache *block_group;
    struct rb_node *node;
    int ret;

    trans = btrfs_start_transaction(tree_root, 0);
    if (IS_ERR(trans))
        return PTR_ERR(trans);

    fs_info->creating_free_space_tree = 1;
    free_space_root = btrfs_create_tree(trans, fs_info,
                                        BTRFS_FREE_SPACE_TREE_OBJECTID);
    if (IS_ERR(free_space_root)) {
        ret = PTR_ERR(free_space_root);
        goto abort;
    }
    fs_info->free_space_root = free_space_root;

    node = rb_first(&fs_info->block_group_cache_tree);
    while (node) {
        block_group = rb_entry(node, struct btrfs_block_group_cache,
                               cache_node);
        ret = populate_free_space_tree(trans, fs_info, block_group);
        if (ret)
            goto abort;
        node = rb_next(node);
    }

    btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE);
    fs_info->creating_free_space_tree = 0;

    ret = btrfs_commit_transaction(trans, tree_root);
    if (ret)
        return ret;

    return 0;

abort:
    fs_info->creating_free_space_tree = 0;
    btrfs_abort_transaction(trans, ret);
    btrfs_end_transaction(trans, tree_root);
    return ret;
}
コード例 #4
0
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;
}
コード例 #5
0
/*
 * copy the data in 'item' into the btree
 */
int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root
		      *root, struct btrfs_key *key, struct btrfs_root_item
		      *item)
{
	struct btrfs_path *path;
	struct extent_buffer *l;
	int ret;
	int slot;
	unsigned long ptr;

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

	ret = btrfs_search_slot(trans, root, key, path, 0, 1);
	if (ret < 0) {
		btrfs_abort_transaction(trans, root, ret);
		goto out;
	}

	if (ret != 0) {
		btrfs_print_leaf(root, path->nodes[0]);
		printk(KERN_CRIT "unable to update root key %llu %u %llu\n",
		       (unsigned long long)key->objectid, key->type,
		       (unsigned long long)key->offset);
		BUG_ON(1);
	}

	l = path->nodes[0];
	slot = path->slots[0];
	ptr = btrfs_item_ptr_offset(l, slot);
	write_extent_buffer(l, item, ptr, sizeof(*item));
	btrfs_mark_buffer_dirty(path->nodes[0]);
out:
	btrfs_free_path(path);
	return ret;
}
コード例 #6
0
ファイル: dedup.c プロジェクト: quadcores/test
int btrfs_dedup_enable(struct btrfs_fs_info *fs_info, u16 type, u16 backend,
		       u64 blocksize, u64 limit)
{
	struct btrfs_dedup_info *dedup_info;
	struct btrfs_root *dedup_root;
	struct btrfs_key key;
	struct btrfs_trans_handle *trans;
	struct btrfs_path *path;
	struct btrfs_dedup_status_item *status;
	int create_tree;
	u64 compat_ro_flag = btrfs_super_compat_ro_flags(fs_info->super_copy);
	int ret = 0;

	/* Sanity check */
	if (blocksize > BTRFS_DEDUP_BLOCKSIZE_MAX ||
	    blocksize < BTRFS_DEDUP_BLOCKSIZE_MIN ||
	    blocksize < fs_info->tree_root->sectorsize ||
	    !is_power_of_2(blocksize))
		return -EINVAL;
	if (type > ARRAY_SIZE(btrfs_dedup_sizes))
		return -EINVAL;
	if (backend >= BTRFS_DEDUP_BACKEND_LAST)
		return -EINVAL;
	if (backend == BTRFS_DEDUP_BACKEND_INMEMORY && limit == 0)
		limit = 4096; /* default value */
	if (backend == BTRFS_DEDUP_BACKEND_ONDISK && limit != 0)
		limit = 0;

	/*
	 * If current fs doesn't support DEDUP feature, don't enable
	 * on-disk dedup.
	 */
	if (!(compat_ro_flag & BTRFS_FEATURE_COMPAT_RO_DEDUP) &&
	    backend == BTRFS_DEDUP_BACKEND_ONDISK)
		return -EINVAL;

	/* Meaningless and unable to enable dedup for RO fs */
	if (fs_info->sb->s_flags & MS_RDONLY)
		return -EINVAL;

	if (fs_info->dedup_info) {
		dedup_info = fs_info->dedup_info;

		/* Check if we are re-enable for different dedup config */
		if (dedup_info->blocksize != blocksize ||
		    dedup_info->hash_type != type ||
		    dedup_info->backend != backend) {
			btrfs_dedup_disable(fs_info);
			goto enable;
		}

		/* On-fly limit change is OK */
		mutex_lock(&dedup_info->lock);
		fs_info->dedup_info->limit_nr = limit;
		mutex_unlock(&dedup_info->lock);
		return 0;
	}

enable:
	create_tree = compat_ro_flag & BTRFS_FEATURE_COMPAT_RO_DEDUP;

	ret = init_dedup_info(fs_info, type, backend, blocksize, limit);
	dedup_info = fs_info->dedup_info;
	if (ret < 0)
		goto out;

	if (!create_tree)
		goto out;

	/* Create dedup tree for status at least */
	path = btrfs_alloc_path();
	if (!path) {
		ret = -ENOMEM;
		goto out;
	}

	trans = btrfs_start_transaction(fs_info->tree_root, 2);
	if (IS_ERR(trans)) {
		ret = PTR_ERR(trans);
		btrfs_free_path(path);
		goto out;
	}

	dedup_root = btrfs_create_tree(trans, fs_info,
				       BTRFS_DEDUP_TREE_OBJECTID);
	if (IS_ERR(dedup_root)) {
		ret = PTR_ERR(dedup_root);
		btrfs_abort_transaction(trans, fs_info->tree_root, ret);
		btrfs_free_path(path);
		goto out;
	}

	dedup_info->dedup_root = dedup_root;

	key.objectid = 0;
	key.type = BTRFS_DEDUP_STATUS_ITEM_KEY;
	key.offset = 0;

	ret = btrfs_insert_empty_item(trans, dedup_root, path, &key,
				      sizeof(*status));
	if (ret < 0) {
		btrfs_abort_transaction(trans, fs_info->tree_root, ret);
		btrfs_free_path(path);
		goto out;
	}
	status = btrfs_item_ptr(path->nodes[0], path->slots[0],
				struct btrfs_dedup_status_item);
	btrfs_set_dedup_status_blocksize(path->nodes[0], status, blocksize);
	btrfs_set_dedup_status_limit(path->nodes[0], status, limit);
	btrfs_set_dedup_status_hash_type(path->nodes[0], status, type);
	btrfs_set_dedup_status_backend(path->nodes[0], status, backend);
	btrfs_mark_buffer_dirty(path->nodes[0]);

	btrfs_free_path(path);
	ret = btrfs_commit_transaction(trans, fs_info->tree_root);

out:
	if (ret < 0) {
		kfree(dedup_info);
		fs_info->dedup_info = NULL;
	}
	return ret;
}
コード例 #7
0
int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
                                  struct btrfs_fs_info *fs_info,
                                  struct btrfs_block_group_cache *block_group,
                                  struct btrfs_path *path)
{
    struct btrfs_root *root = fs_info->free_space_root;
    struct btrfs_free_space_info *info;
    struct btrfs_key key, found_key;
    struct extent_buffer *leaf;
    unsigned long *bitmap;
    u64 start, end;
    /* Initialize to silence GCC. */
    u64 extent_start = 0;
    u64 offset;
    u32 bitmap_size, flags, expected_extent_count;
    int prev_bit = 0, bit, bitnr;
    u32 extent_count = 0;
    int done = 0, nr;
    int ret;

    bitmap_size = free_space_bitmap_size(block_group->key.offset,
                                         block_group->sectorsize);
    bitmap = alloc_bitmap(bitmap_size);
    if (!bitmap) {
        ret = -ENOMEM;
        goto out;
    }

    start = block_group->key.objectid;
    end = block_group->key.objectid + block_group->key.offset;

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

    while (!done) {
        ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1);
        if (ret)
            goto out;

        leaf = path->nodes[0];
        nr = 0;
        path->slots[0]++;
        while (path->slots[0] > 0) {
            btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0] - 1);

            if (found_key.type == BTRFS_FREE_SPACE_INFO_KEY) {
                ASSERT(found_key.objectid == block_group->key.objectid);
                ASSERT(found_key.offset == block_group->key.offset);
                done = 1;
                break;
            } else if (found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) {
                unsigned long ptr;
                char *bitmap_cursor;
                u32 bitmap_pos, data_size;

                ASSERT(found_key.objectid >= start);
                ASSERT(found_key.objectid < end);
                ASSERT(found_key.objectid + found_key.offset <= end);

                bitmap_pos = div_u64(found_key.objectid - start,
                                     block_group->sectorsize *
                                     BITS_PER_BYTE);
                bitmap_cursor = ((char *)bitmap) + bitmap_pos;
                data_size = free_space_bitmap_size(found_key.offset,
                                                   block_group->sectorsize);

                ptr = btrfs_item_ptr_offset(leaf, path->slots[0] - 1);
                read_extent_buffer(leaf, bitmap_cursor, ptr,
                                   data_size);

                nr++;
                path->slots[0]--;
            } else {
                ASSERT(0);
            }
        }

        ret = btrfs_del_items(trans, root, path, path->slots[0], nr);
        if (ret)
            goto out;
        btrfs_release_path(path);
    }

    info = search_free_space_info(trans, fs_info, block_group, path, 1);
    if (IS_ERR(info)) {
        ret = PTR_ERR(info);
        goto out;
    }
    leaf = path->nodes[0];
    flags = btrfs_free_space_flags(leaf, info);
    flags &= ~BTRFS_FREE_SPACE_USING_BITMAPS;
    btrfs_set_free_space_flags(leaf, info, flags);
    expected_extent_count = btrfs_free_space_extent_count(leaf, info);
    btrfs_mark_buffer_dirty(leaf);
    btrfs_release_path(path);

    offset = start;
    bitnr = 0;
    while (offset < end) {
        bit = !!test_bit(bitnr, bitmap);
        if (prev_bit == 0 && bit == 1) {
            extent_start = offset;
        } else if (prev_bit == 1 && bit == 0) {
            key.objectid = extent_start;
            key.type = BTRFS_FREE_SPACE_EXTENT_KEY;
            key.offset = offset - extent_start;

            ret = btrfs_insert_empty_item(trans, root, path, &key, 0);
            if (ret)
                goto out;
            btrfs_release_path(path);

            extent_count++;
        }
        prev_bit = bit;
        offset += block_group->sectorsize;
        bitnr++;
    }
    if (prev_bit == 1) {
        key.objectid = extent_start;
        key.type = BTRFS_FREE_SPACE_EXTENT_KEY;
        key.offset = end - extent_start;

        ret = btrfs_insert_empty_item(trans, root, path, &key, 0);
        if (ret)
            goto out;
        btrfs_release_path(path);

        extent_count++;
    }

    if (extent_count != expected_extent_count) {
        btrfs_err(fs_info, "incorrect extent count for %llu; counted %u, expected %u",
                  block_group->key.objectid, extent_count,
                  expected_extent_count);
        ASSERT(0);
        ret = -EIO;
        goto out;
    }

    ret = 0;
out:
    kvfree(bitmap);
    if (ret)
        btrfs_abort_transaction(trans, ret);
    return ret;
}
コード例 #8
0
int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
                                  struct btrfs_fs_info *fs_info,
                                  struct btrfs_block_group_cache *block_group,
                                  struct btrfs_path *path)
{
    struct btrfs_root *root = fs_info->free_space_root;
    struct btrfs_free_space_info *info;
    struct btrfs_key key, found_key;
    struct extent_buffer *leaf;
    unsigned long *bitmap;
    char *bitmap_cursor;
    u64 start, end;
    u64 bitmap_range, i;
    u32 bitmap_size, flags, expected_extent_count;
    u32 extent_count = 0;
    int done = 0, nr;
    int ret;

    bitmap_size = free_space_bitmap_size(block_group->key.offset,
                                         block_group->sectorsize);
    bitmap = alloc_bitmap(bitmap_size);
    if (!bitmap) {
        ret = -ENOMEM;
        goto out;
    }

    start = block_group->key.objectid;
    end = block_group->key.objectid + block_group->key.offset;

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

    while (!done) {
        ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1);
        if (ret)
            goto out;

        leaf = path->nodes[0];
        nr = 0;
        path->slots[0]++;
        while (path->slots[0] > 0) {
            btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0] - 1);

            if (found_key.type == BTRFS_FREE_SPACE_INFO_KEY) {
                ASSERT(found_key.objectid == block_group->key.objectid);
                ASSERT(found_key.offset == block_group->key.offset);
                done = 1;
                break;
            } else if (found_key.type == BTRFS_FREE_SPACE_EXTENT_KEY) {
                u64 first, last;

                ASSERT(found_key.objectid >= start);
                ASSERT(found_key.objectid < end);
                ASSERT(found_key.objectid + found_key.offset <= end);

                first = div_u64(found_key.objectid - start,
                                block_group->sectorsize);
                last = div_u64(found_key.objectid + found_key.offset - start,
                               block_group->sectorsize);
                bitmap_set(bitmap, first, last - first);

                extent_count++;
                nr++;
                path->slots[0]--;
            } else {
                ASSERT(0);
            }
        }

        ret = btrfs_del_items(trans, root, path, path->slots[0], nr);
        if (ret)
            goto out;
        btrfs_release_path(path);
    }

    info = search_free_space_info(trans, fs_info, block_group, path, 1);
    if (IS_ERR(info)) {
        ret = PTR_ERR(info);
        goto out;
    }
    leaf = path->nodes[0];
    flags = btrfs_free_space_flags(leaf, info);
    flags |= BTRFS_FREE_SPACE_USING_BITMAPS;
    btrfs_set_free_space_flags(leaf, info, flags);
    expected_extent_count = btrfs_free_space_extent_count(leaf, info);
    btrfs_mark_buffer_dirty(leaf);
    btrfs_release_path(path);

    if (extent_count != expected_extent_count) {
        btrfs_err(fs_info, "incorrect extent count for %llu; counted %u, expected %u",
                  block_group->key.objectid, extent_count,
                  expected_extent_count);
        ASSERT(0);
        ret = -EIO;
        goto out;
    }

    bitmap_cursor = (char *)bitmap;
    bitmap_range = block_group->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS;
    i = start;
    while (i < end) {
        unsigned long ptr;
        u64 extent_size;
        u32 data_size;

        extent_size = min(end - i, bitmap_range);
        data_size = free_space_bitmap_size(extent_size,
                                           block_group->sectorsize);

        key.objectid = i;
        key.type = BTRFS_FREE_SPACE_BITMAP_KEY;
        key.offset = extent_size;

        ret = btrfs_insert_empty_item(trans, root, path, &key,
                                      data_size);
        if (ret)
            goto out;

        leaf = path->nodes[0];
        ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
        write_extent_buffer(leaf, bitmap_cursor, ptr,
                            data_size);
        btrfs_mark_buffer_dirty(leaf);
        btrfs_release_path(path);

        i += extent_size;
        bitmap_cursor += data_size;
    }

    ret = 0;
out:
    kvfree(bitmap);
    if (ret)
        btrfs_abort_transaction(trans, ret);
    return ret;
}
コード例 #9
0
int remove_block_group_free_space(struct btrfs_trans_handle *trans,
                                  struct btrfs_fs_info *fs_info,
                                  struct btrfs_block_group_cache *block_group)
{
    struct btrfs_root *root = fs_info->free_space_root;
    struct btrfs_path *path;
    struct btrfs_key key, found_key;
    struct extent_buffer *leaf;
    u64 start, end;
    int done = 0, nr;
    int ret;

    if (!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE))
        return 0;

    if (block_group->needs_free_space) {
        /* We never added this block group to the free space tree. */
        return 0;
    }

    path = btrfs_alloc_path();
    if (!path) {
        ret = -ENOMEM;
        goto out;
    }

    start = block_group->key.objectid;
    end = block_group->key.objectid + block_group->key.offset;

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

    while (!done) {
        ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1);
        if (ret)
            goto out;

        leaf = path->nodes[0];
        nr = 0;
        path->slots[0]++;
        while (path->slots[0] > 0) {
            btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0] - 1);

            if (found_key.type == BTRFS_FREE_SPACE_INFO_KEY) {
                ASSERT(found_key.objectid == block_group->key.objectid);
                ASSERT(found_key.offset == block_group->key.offset);
                done = 1;
                nr++;
                path->slots[0]--;
                break;
            } else if (found_key.type == BTRFS_FREE_SPACE_EXTENT_KEY ||
                       found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) {
                ASSERT(found_key.objectid >= start);
                ASSERT(found_key.objectid < end);
                ASSERT(found_key.objectid + found_key.offset <= end);
                nr++;
                path->slots[0]--;
            } else {
                ASSERT(0);
            }
        }

        ret = btrfs_del_items(trans, root, path, path->slots[0], nr);
        if (ret)
            goto out;
        btrfs_release_path(path);
    }

    ret = 0;
out:
    btrfs_free_path(path);
    if (ret)
        btrfs_abort_transaction(trans, ret);
    return ret;
}