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; }
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; }
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; }
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; }
/* * 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; }
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; }
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; }
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; }
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; }